classdef mbdWorkflowTool_Redesigned < handle
    % MBD Platform v2.0 - Grid Layout System
    % 基于uigridlayout的现代化MBD工作流工具
    % 支持完全响应式布局，自动适配窗口大小变化
    % Qt风格布局管理：QHBoxLayout、QVBoxLayout、QGridLayout、QFormLayout
    
    properties (Access = public)
        % Main UI Components
        MainFigure                      matlab.ui.Figure
        MainGrid                        matlab.ui.container.GridLayout  % 主网格布局系统
        TopControlsPanel                matlab.ui.container.Panel
        TopGrid                         matlab.ui.container.GridLayout  % 顶部控件网格布局 (类似QHBoxLayout)
        AuxPanel                  matlab.ui.container.Panel
        DashboardGrid                   matlab.ui.container.GridLayout  % 仪表盘网格布局 (类似QVBoxLayout)
        TabGroup                        matlab.ui.container.TabGroup
        
        % Top Controls
        titleLabel                      matlab.ui.control.Label
        im                              matlab.ui.control.Image
        CurrentModelDropDownLabel       matlab.ui.control.Label
        CurrentModelDropDown            matlab.ui.control.DropDown
        RefreshModelButton              matlab.ui.control.Button
        TopModelCheckBox                matlab.ui.control.CheckBox
        TopOpenModelButton              matlab.ui.control.Button
        SwitchtoworkfolderButton        matlab.ui.control.Button
        OpenICDButton                   matlab.ui.control.Button
        OpenSystemArchitectureButton    matlab.ui.control.Button
        OpenProjectDocArchitectureButton matlab.ui.control.Button
        MessageLabel                    matlab.ui.control.Label
        MessagesTextArea                matlab.ui.control.TextArea
        
        % Model Analysis Tab (Enhanced)
        ModelAnalysisTab                matlab.ui.container.Tab
        ModelAnalysisGrid               matlab.ui.container.GridLayout
        OpenModelAdvisorButton          matlab.ui.control.Button
        OpenReportButton                matlab.ui.control.Button
        ModelMetricsOpenReportButton    matlab.ui.control.Button
        ModelMetricsLamp                matlab.ui.control.Lamp
        SLDVPanel                       matlab.ui.container.Panel
        OpenSLDVButton                  matlab.ui.control.Button
        RunSLDVButton                   matlab.ui.control.Button
        SLDVLamp                        matlab.ui.control.Lamp
        SLDVOpenReportButton            matlab.ui.control.Button
        
        % Model Checklist Panel
        ModelChecklistPanel             matlab.ui.container.Panel
        CreateModelChecklistButton      matlab.ui.control.Button
        OpenModelChecklistButton        matlab.ui.control.Button
        ModelChecklistLamp              matlab.ui.control.Lamp
        
        % Menu System
        MenuBar                         matlab.ui.container.Menu
        ColourThemeMenu                 matlab.ui.container.Menu
        DarkModeMenu                    matlab.ui.container.Menu
        LightModeMenu                   matlab.ui.container.Menu
        ManualMenu                      matlab.ui.container.Menu
        UserGuideMenu                   matlab.ui.container.Menu
        AboutMenu                       matlab.ui.container.Menu
        HelpMenu                        matlab.ui.container.Menu
        HelpUserGuideMenu               matlab.ui.container.Menu
        
        % Requirements Management Tab
        RequirementsTab                 matlab.ui.container.Tab
        RequirementsGrid                matlab.ui.container.GridLayout % Requirements标签页网格布局 (类似QGridLayout)
        ImportCSVButton                 matlab.ui.control.Button
        ImportReqIFButton               matlab.ui.control.Button
        ExportToWordButton              matlab.ui.control.Button
        ResizableGrid                   matlab.ui.container.GridLayout  % 可调整宽度的网格布局
        Divider1                        matlab.ui.container.Panel       % 拖拽分割线
        CurrentLayoutIndex              double                           % 当前布局索引
        LayoutPresets                   cell                            % 布局预设数组
        ReqListPanel                    matlab.ui.container.Panel
        RequirementsTree                matlab.ui.container.Tree
        RefreshReqTreeButton            matlab.ui.control.Button
        OpenRequirementFileButton       matlab.ui.control.Button
        ReqStatusPanel                  matlab.ui.container.Panel
        ReqStatusTabGroup               matlab.ui.container.TabGroup
        ViewTraceMatrixButton           matlab.ui.control.Button
        AddReqTabButton                 matlab.ui.control.Button
        DeleteReqTabButton              matlab.ui.control.Button
        
        % Dynamic Requirements Tab Management
        DynamicReqTabs                  struct  % 动态Tab数据结构：{name, tab, table, filterPanel, loadButton}
        
        % Requirements Editor Filter Components - 不再需要
        % 所有过滤器组件都在动态Tab内部，不存储在app属性中
        
        % Requirements Editor Action Buttons
        
        % Information Text Labels for Dark Mode
        divider1Label                   matlab.ui.control.Label         % 分隔符拖拽提示
        polyspaceInfo                   matlab.ui.control.Label         % Polyspace说明文本
        qualityInfo                     matlab.ui.control.Label         % 代码质量说明文本
        authoringInfo                   matlab.ui.control.Label         % 测试编写说明文本
        docInfo                         matlab.ui.control.Label         % 文档说明文本
        
        % Requirements Data Storage - 不再需要，数据存储在动态Tab结构中
        % 所有Tab（包括defaultReq）都是动态创建的
        
        % Model Design and Analysis Main Tab
        ModelDesignAnalysisTab          matlab.ui.container.Tab         % Model Design and Analysis主标签页
        ModelDesignAnalysisGrid         matlab.ui.container.GridLayout  % 主布局网格（左右分栏）
        DesignModelsPanel               matlab.ui.container.Panel       % Design Models面板（左侧30%）
        SubTabPanel                     matlab.ui.container.Panel       % 二级TabGroup面板（右侧70%）
        SubTabGroup                     matlab.ui.container.TabGroup    % 二级标签页组
        
        % Model Management Tab (二级)
        ModelManagementTab              matlab.ui.container.Tab
        ModelGrid                       matlab.ui.container.GridLayout % Model标签页网格布局
        ModelTreePanel                  matlab.ui.container.Panel      % 模型树面板（将移动到DesignModelsPanel）
        ModelTree                       matlab.ui.container.Tree       % 模型树
        DesignModelsNode                matlab.ui.container.TreeNode   % 根节点
        ModelActionsPanel               matlab.ui.container.Panel      % 模型操作面板
        SelectModelButton               matlab.ui.control.Button       % 选择模型按钮
        OpenModelButton                 matlab.ui.control.Button       % 打开模型按钮
        DeleteModelButton               matlab.ui.control.Button       % 删除模型按钮
        RefreshTreeButton               matlab.ui.control.Button       % 刷新树按钮
        ModelCreationPanel              matlab.ui.container.Panel
        NewModelButton                  matlab.ui.control.Button
        ImportModelButton               matlab.ui.control.Button
        ModelTemplateDropDown           matlab.ui.control.DropDown
        
        % Model Dashboard Panel
        ModelDashboardPanel             matlab.ui.container.Panel
        RefreshModelDashboardButton     matlab.ui.control.Button
        OpenModelDashboardButton        matlab.ui.control.Button
        
        % Model Management Extended Features (integrated from Setup)
        ModelReportsPanel               matlab.ui.container.Panel
        SDDReportButton                 matlab.ui.control.Button
        SDDLamp                         matlab.ui.control.Lamp
        OpenSDDButton                   matlab.ui.control.Button
        ModelDependencyPanel            matlab.ui.container.Panel
        ViewModelDependencyButton       matlab.ui.control.Button
        ViewFileDependencyButton        matlab.ui.control.Button
        OpenSLDDButton                  matlab.ui.control.Button
        RunAllButton                    matlab.ui.control.Button
        ModelStandardsPanel             matlab.ui.container.Panel
        RunModelAdvisorButton           matlab.ui.control.Button
        ModelAdvisorLamp                matlab.ui.control.Lamp
        ModelMetricsPanel               matlab.ui.container.Panel
        ComputeModelMetricsButton       matlab.ui.control.Button
        
        % Code Generation Tab
        CodeGenerationTab               matlab.ui.container.Tab
        CodeGrid                        matlab.ui.container.GridLayout % Code标签页网格布局
        RunAllCodeAnalysisButton        matlab.ui.control.Button
        CodeGenPanel                    matlab.ui.container.Panel
        GenerateCodeButton              matlab.ui.control.Button
        OpenCodeReportButton            matlab.ui.control.Button
        CodeGenLamp                     matlab.ui.control.Lamp
        CodeInspectionPanel             matlab.ui.container.Panel
        RunCodeInspectorButton          matlab.ui.control.Button
        CodeInspectorLamp               matlab.ui.control.Lamp
        OpenCodeInspectorReportButton   matlab.ui.control.Button
        CodeAnalysisPanel               matlab.ui.container.Panel
        RunBugFinderButton              matlab.ui.control.Button
        BugFinderLamp                   matlab.ui.control.Lamp
        OpenBugFinderReportButton       matlab.ui.control.Button
        RunCodeStandardsButton          matlab.ui.control.Button
        CodeStandardsLamp               matlab.ui.control.Lamp
        OpenCodeStandardsReportButton   matlab.ui.control.Button
        RunCodeProverButton             matlab.ui.control.Button
        CodeProverLamp                  matlab.ui.control.Lamp
        OpenCodeProverReportButton      matlab.ui.control.Button
        CodeQualityPanel                matlab.ui.container.Panel
        ComputeCodeMetricsButton        matlab.ui.control.Button
        CodeMetricsLamp                 matlab.ui.control.Lamp
        OpenCodeMetricsReportButton     matlab.ui.control.Button
        
        % Testing Management Tab
        TestingTab                      matlab.ui.container.Tab
        TestingGrid                     matlab.ui.container.GridLayout % Testing标签页网格布局
        TestExecutionPanel              matlab.ui.container.Panel
        TestModeDropDown                matlab.ui.control.DropDown
        OpenTestManagerButton           matlab.ui.control.Button
        HLRTestPanel                    matlab.ui.container.Panel
        RunHLRTestButton                matlab.ui.control.Button
        OpenHLRReportButton             matlab.ui.control.Button
        OpenHLRCoverageButton           matlab.ui.control.Button
        HLRTestLamp                     matlab.ui.control.Lamp
        HLRCoverageLamp                 matlab.ui.control.Lamp
        LLRTestPanel                    matlab.ui.container.Panel
        RunLLRTestButton                matlab.ui.control.Button
        OpenLLRReportButton             matlab.ui.control.Button
        OpenLLRCoverageButton           matlab.ui.control.Button
        LLRTestLamp                     matlab.ui.control.Lamp
        LLRCoverageLamp                 matlab.ui.control.Lamp
        CoveragePanel                   matlab.ui.container.Panel
        MergeCoverageButton             matlab.ui.control.Button
        CoverageGauge                   matlab.ui.control.Label
        TestAuthoringToolButton         matlab.ui.control.Button
        TestManagementPanel             matlab.ui.container.Panel
        RefreshTestCasesButton          matlab.ui.control.Button
        TestCaseTable                   matlab.ui.control.Table
        
        % Reports & Deliverables Tab
        ReportsTab                      matlab.ui.container.Tab
        ReportsGrid                     matlab.ui.container.GridLayout % Reports标签页网格布局
        ReportGenerationPanel           matlab.ui.container.Panel
        GenerateTestReportButton        matlab.ui.control.Button
        GenerateCoverageReportButton    matlab.ui.control.Button
        GenerateSDDButton               matlab.ui.control.Button
        DeliverablePanel                matlab.ui.container.Panel
        ExportDeliverableButton         matlab.ui.control.Button
        DO178ObjectivesButton           matlab.ui.control.Button
        ReportStatusPanel               matlab.ui.container.Panel
        ReportStatusTable               matlab.ui.control.Table
        DocumentationPanel              matlab.ui.container.Panel
        ViewDocumentationButton         matlab.ui.control.Button
        
        % Project Configuration Tab
        ProjectConfigTab                matlab.ui.container.Tab
        ProjectConfigGrid               matlab.ui.container.GridLayout % Project Config标签页网格布局
        ConfigFileTreePanel             matlab.ui.container.Panel      % 左侧配置文件树面板
        ConfigTree                      matlab.ui.container.Tree       % 配置文件树
        ConfigInfoPanel                 matlab.ui.container.Panel      % 右侧信息显示面板
        RefreshConfigButton             matlab.ui.control.Button       % 刷新配置文件按钮
        OpenConfigFileButton            matlab.ui.control.Button       % 打开选中配置文件按钮
        ConfigFileInfoLabel             matlab.ui.control.Label        % 文件信息显示标签
        ConfigFilePathLabel             matlab.ui.control.Label        % 文件路径显示标签
        
        % User Management Components
        UserNameLabel                   matlab.ui.control.Label        % 用户名显示标签
        UserNameEditField               matlab.ui.control.EditField    % 用户名编辑框
        SaveUserNameButton              matlab.ui.control.Button       % 保存用户名按钮
        userName                        string                         % 当前用户名（会话变量，供全局访问）
        
        % Version Information Components
        FileInfoPanel                   matlab.ui.container.Panel      % 文件夹信息面板
        UserInfoPanel                   matlab.ui.container.Panel      % 用户信息面板  
        VersionInfoPanel                matlab.ui.container.Panel      % 工具版本信息面板
        SoftwareVersionLabel            matlab.ui.control.Label        % 软件版本标签
        SoftwareVersionEditField        matlab.ui.control.EditField    % 软件版本显示框（只读）
        ProjectConfigVersionLabel       matlab.ui.control.Label        % 项目配置版本标签
        ProjectConfigVersionEditField   matlab.ui.control.EditField    % 项目配置版本显示框（只读）
        ToolVersionLabel                matlab.ui.control.Label        % 工具版本标签
        ToolVersionEditField            matlab.ui.control.EditField    % 工具版本显示框（只读）
        
        % ALM Tab
        ALMTab                          matlab.ui.container.Tab
        ALMGrid                         matlab.ui.container.GridLayout  % ALM标签页网格布局
        CRTablePanel                    matlab.ui.container.Panel       % 左侧CR表格面板
        CRTable                         matlab.ui.control.Table         % CR表格
        PRTablePanel                    matlab.ui.container.Panel       % 中间PR表格面板
        PRTable                         matlab.ui.control.Table         % PR表格
        ALMButtonPanel                  matlab.ui.container.Panel       % 右侧按钮面板
        OpenSelectedPRButton            matlab.ui.control.Button        % 打开选中PR按钮
        OpenSelectedCRButton            matlab.ui.control.Button        % 打开选中CR按钮
        SubmitPRButton                  matlab.ui.control.Button        % 提交PR按钮
        ApprovePRButton                 matlab.ui.control.Button        % 审批PR按钮（仅admin）
        SubmitCRButton                  matlab.ui.control.Button        % 提交CR按钮
        ApproveCRButton                 matlab.ui.control.Button        % 审批CR按钮（仅admin）
        RefreshPRCRButton               matlab.ui.control.Button        % 刷新PR/CR按钮
        
        % License Status
        LicenseStatus                   struct  % 存储各工具箱许可证状态
        
        % Theme Management
        CurrentTheme                    string  % 当前主题状态："light" 或 "dark"
        ThemeMgr                                % 主题管理服务实例
        
        % Service Layer - 业务逻辑服务实例
        ALMService                              % ALM集成服务实例（协调所有ALM业务逻辑）
        ReqService                              % 需求管理服务实例（协调所有需求管理业务逻辑）
        ModelService                            % 模型管理服务实例（协调所有模型管理业务逻辑）
        TestService                             % 测试管理服务实例（HLR/LLR测试MIL/SIL/PIL、测试生成、覆盖率合并、环境清理）- 使用TestManagementService
        CodeService                             % 代码分析服务实例（协调所有代码分析业务逻辑）
        
        % New Service Layer (2025-10-14 重构)
        ModelStaticAnalysisService              % 模型静态分析服务实例（模型标准检查、指标计算、SDD生成等）
        CodeTestingService                      % 代码测试服务实例（代码级HLR/LLR测试、代码覆盖率合并）
        
        % Logging Service (2025-10-19 新增)
        logger                                  % LoggingService实例（GUI层日志管理）
        ProjectManagementService                % 项目管理服务实例（模型创建、删除、项目结构管理）
        
        % ALM Filter Components
        CRFilterPanel                   matlab.ui.container.Panel
        PRFilterPanel                   matlab.ui.container.Panel
        PRStatusFilter                  matlab.ui.control.DropDown
        PRSeverityFilter                matlab.ui.control.DropDown
        PRSearchField                   matlab.ui.control.EditField
        PRResetFilterButton             matlab.ui.control.Button
        CRStatusFilter                  matlab.ui.control.DropDown
        CRPriorityFilter                matlab.ui.control.DropDown
        CRSearchField                   matlab.ui.control.EditField
        CRResetFilterButton             matlab.ui.control.Button
        
        % ALM Original Data Storage (用于过滤后恢复数据)
        PROriginalData                  % PR原始数据（未过滤）
        CROriginalData                  % CR原始数据（未过滤）
    end
    
    methods (Access = public)
        function app = mbdWorkflowTool_Redesigned(varargin)
            % 构造函数 - 创建现代化Grid Layout UI系统
            
            % 从配置文件读取默认主题设置（3通过yamltool读取yaml文件）
            defaultTheme = 'light';  % fallback默认值
            try
                % 获取配置文件路径
                configPath = fullfile(fileparts(mfilename('fullpath')), 'mbdWorkflowTool_config.yml');

                % 读取配置文件（3条）
                config = yamlread(configPath);
                
                % 提取主题配置
                if isfield(config, 'theme') && isfield(config.theme, 'default_mode')
                    defaultTheme = config.theme.default_mode;
                end
            catch ME
                % 配置文件读取失败时使用fallback（用户明确允许）
                warning('mbdWorkflowTool:ConfigReadFailed', ...
                    '无法读取配置文件或配置项不存在，使用默认浅色主题。错误信息: %s', ME.message);
            end
            
            % 初始化主题状态
            app.CurrentTheme = string(defaultTheme);
            
            % 初始化主题管理服务
            app.ThemeMgr = ThemeManager('Verbose', false);
            
            % 初始化日志服务（2025-10-19 新增）
            try
                app.logger = LoggingService.getInstance();
                fprintf('✅ GUI日志服务已初始化\n');
            catch ME
                warning('LoggingService:Unavailable', 'GUI日志服务不可用: %s', ME.message);
                app.logger = [];
            end
            
            % 初始化动态需求Tab结构体
            app.DynamicReqTabs = struct();
            
            app.createUIComponents();
            checkMBDToolboxLicenses(app);  % 检查许可证并配置按钮状态
            app.setupCallbacks();
            app.initializeSampleData();
            
            % 在显示窗口前应用从配置文件读取的默认主题
            % 4使用ThemeManager强制应用主题（架构分离）
            % 无论是否支持原生主题，都统一使用ThemeManager来确保主题被强制应用
            app.ThemeMgr.applyTheme(app.MainFigure, char(app.CurrentTheme));
            
            % 如果支持原生主题，同步设置Theme属性
            if isprop(app.MainFigure, 'Theme')
                app.MainFigure.Theme = app.CurrentTheme;
            end
            
            % 显示主窗口 - Grid Layout系统已完全构建
            app.MainFigure.Visible = 'on';
        end
    end
    methods (Access = private)
        function createUIComponents(app)
            % 创建所有UI组件 - 基于Grid Layout的现代化布局系统
            app.createMainFigure();
            app.createMenuSystem();
            app.createMainControls();
            app.createDashboard();
            app.createTabGroup();
            app.createRequirementsTab();
            app.createModelDesignAnalysisTab();
            app.createReportsTab();
            app.createProjectConfigTab();
            app.createALMTab();
            
            % 初始化管理员权限（在所有UI组件创建完成后，确保不阻止app启动）
            app.initializeAdminPermissions();
        end
        
        function createMainFigure(app)
            % 创建主窗口 - 基于uigridlayout的现代化布局
            app.MainFigure = uifigure('Visible', 'off');
            app.MainFigure.Position = [100 100 1200 850];
            app.MainFigure.Name = 'MBD Platform For DO331';
            app.MainFigure.Resize = 'on';
            
            % 如果支持MATLAB 2025a原生主题功能，设置主题回调
            if isprop(app.MainFigure, 'Theme')
                % 设置主题变化回调函数
                app.MainFigure.ThemeChangedFcn = @(src, event) app.onThemeChanged(src, event);
                % 注意：不在这里设置主题，将在构造函数中统一应用配置的默认主题
            end

            % 创建主网格布局 (2行2列)
            app.MainGrid = uigridlayout(app.MainFigure, [2, 2]);
            app.MainGrid.RowHeight = {'fit', '1x'};           % 顶部自适应，底部占剩余空间
            app.MainGrid.ColumnWidth = {'1x', 'fit'};         % 左侧占主要空间，右侧适应内容
            app.MainGrid.Padding = [10, 10, 10, 10];          % 边距
            app.MainGrid.RowSpacing = 10;                      % 行间距
            app.MainGrid.ColumnSpacing = 10;                   % 列间距
        end
        
        function createMenuSystem(app)
            % 创建菜单系统
            app.MenuBar = uimenu(app.MainFigure);
            app.MenuBar.Text = 'Options';
            
            % 创建颜色主题菜单
            app.ColourThemeMenu = uimenu(app.MenuBar);
            app.ColourThemeMenu.Text = 'Colour Theme';
            
            % 动态创建主题菜单项（3通过yamltool读取yaml文件）
            app.createDynamicThemeMenus();
            
            % 创建Manual菜单项
            app.ManualMenu = uimenu(app.MenuBar);
            app.ManualMenu.Text = 'Manual';
            app.ManualMenu.MenuSelectedFcn = @app.ManualMenuSelected;
            
            app.UserGuideMenu = uimenu(app.MenuBar);
            app.UserGuideMenu.Text = 'User Guide';
            
            app.AboutMenu = uimenu(app.MenuBar);
            app.AboutMenu.Text = 'About';
            
            % 创建Help菜单（放在最右边）
            app.HelpMenu = uimenu(app.MainFigure);
            app.HelpMenu.Text = 'Help';
            
            % 在Help菜单下创建子菜单
            app.HelpUserGuideMenu = uimenu(app.HelpMenu);
            app.HelpUserGuideMenu.Text = 'MBD PLATFORM user guide';
            app.HelpUserGuideMenu.MenuSelectedFcn = @app.HelpUserGuideMenuSelected;
        end
        
        function createDynamicThemeMenus(app)
            % 动态创建主题菜单项
            
            % 从ThemeManager获取可用主题列表
            availableThemes = app.ThemeMgr.getAvailableThemes();
            
            % 为每个主题创建菜单项
            for i = 1:numel(availableThemes)
                theme = availableThemes(i);
                
                % 创建菜单项（不使用emoji）
                menuItem = uimenu(app.ColourThemeMenu);
                menuItem.Text = theme.displayName;
                
                % 设置回调函数 - 使用匿名函数传递主题名称
                menuItem.MenuSelectedFcn = @(src, event) app.ThemeMenuSelected(theme.name);
            end
        end
        
        function createMainControls(app)
            % 创建主要控件 - 基于uigridlayout的现代化布局（类似QHBoxLayout）

            % 创建顶部控制面板 - 横跨主网格第1行的两列
            app.TopControlsPanel = uipanel(app.MainGrid);
            app.TopControlsPanel.Layout.Row = 1;
            app.TopControlsPanel.Layout.Column = [1, 2];  % 横跨两列
            app.TopControlsPanel.Title = '';
            app.TopControlsPanel.BorderType = 'none';

            % 在顶部面板内创建水平网格布局 (2行8列，类似QHBoxLayout)
            app.TopGrid = uigridlayout(app.TopControlsPanel, [2, 8]);
            app.TopGrid.RowHeight = {30, 'fit'};  % 2行：上行主要控件，下行消息区
            app.TopGrid.ColumnWidth = {'fit', 200, 'fit', 'fit', 'fit', 40, '1x', 'fit'};  % 标题|logo|模型选择|刷新按钮(固定40px)|复选框|弹性空间|打开模型|工作目录
            app.TopGrid.Padding = [10, 10, 10, 10];
            app.TopGrid.RowSpacing = 5;
            app.TopGrid.ColumnSpacing = 10;

            % 标题 [1,1]
            app.titleLabel = uilabel(app.TopGrid);
            app.titleLabel.Layout.Row = 1;
            app.titleLabel.Layout.Column = 1;
            app.titleLabel.Text = 'DO331 MBD Platform';
            app.titleLabel.FontWeight = 'bold';
            app.titleLabel.FontSize = 16;
            app.titleLabel.FontColor = [0 0.4 0.7];
            % zephyr图标 [1,2]
            app.im = uiimage(app.TopGrid);
            app.im.Layout.Row = 1;
            app.im.Layout.Column = 2;
            app.im.ImageSource  = 'Zephyrs.png';
            app.im.HorizontalAlignment = 'left';
            %app.im.Position = [0 20 0 10];
     

            % 模型选择区域 [1,4-9] 全部靠右
            app.CurrentModelDropDownLabel = uilabel(app.TopGrid);
            app.CurrentModelDropDownLabel.Layout.Row = 1;
            app.CurrentModelDropDownLabel.Layout.Column = 4;
            app.CurrentModelDropDownLabel.Text = 'Current Model';

            app.CurrentModelDropDown = uidropdown(app.TopGrid);
            app.CurrentModelDropDown.Layout.Row = 1;
            app.CurrentModelDropDown.Layout.Column = 5;
            app.CurrentModelDropDown.Items = {'正在检测模型...'};
            app.CurrentModelDropDown.Value = '正在检测模型...';

            % 刷新模型按钮 [1,6] - 小按钮，不要太宽
            app.RefreshModelButton = uibutton(app.TopGrid, 'push');
            app.RefreshModelButton.Layout.Row = 1;
            app.RefreshModelButton.Layout.Column = 6;
            app.RefreshModelButton.Text = '🔄';
            app.RefreshModelButton.Tooltip = '刷新模型列表';
            app.RefreshModelButton.BackgroundColor = [0.9 0.9 0.9];
            app.RefreshModelButton.FontSize = 10; % 减小字体以减小按钮宽度
            app.RefreshModelButton.Icon = ''; % 确保没有额外的图标占用空间

            app.TopModelCheckBox = uicheckbox(app.TopGrid);
            app.TopModelCheckBox.Layout.Row = 1;
            app.TopModelCheckBox.Layout.Column = 7;
            app.TopModelCheckBox.Text = 'Top Model';
            app.TopModelCheckBox.Enable = 'off';

            % 顶部打开模型按钮 [1,8]
            app.TopOpenModelButton = uibutton(app.TopGrid, 'push');
            app.TopOpenModelButton.Layout.Row = 1;
            app.TopOpenModelButton.Layout.Column = 8;
            app.TopOpenModelButton.Text = '📁 Open Model';
            app.TopOpenModelButton.BackgroundColor = [0.2 0.6 0.9];
            app.TopOpenModelButton.FontColor = [1 1 1];
            app.TopOpenModelButton.FontWeight = 'bold';
            app.TopOpenModelButton.Tooltip = '打开选中的模型文件（全局快捷按钮）';

            app.SwitchtoworkfolderButton = uibutton(app.TopGrid, 'push');
            app.SwitchtoworkfolderButton.Layout.Row = 1;
            app.SwitchtoworkfolderButton.Layout.Column = 9;
            app.SwitchtoworkfolderButton.Text = 'Switch to work folder';
            app.SwitchtoworkfolderButton.BackgroundColor = [0.8 0.9 1];

            % 消息区域 [2,1-9] 横跨全部列
            app.MessageLabel = uilabel(app.TopGrid);
            app.MessageLabel.Layout.Row = 2;
            app.MessageLabel.Layout.Column = 1;
            app.MessageLabel.Text = 'Messages';
            app.MessageLabel.FontWeight = 'bold';

            app.MessagesTextArea = uitextarea(app.TopGrid);
            app.MessagesTextArea.Layout.Row = 2;
            app.MessagesTextArea.Layout.Column = [2, 9];  % 横跨多列
            app.MessagesTextArea.Value = '欢迎使用MBD Platform v2.0 - Grid Layout System!';
            app.MessagesTextArea.Editable = 'off';
        end
        
        function createDashboard(app)
            % 创建仪表盘区域 - 基于Grid布局

            % Dashboard面板放在主网格的[2,2]位置（右下角）
            app.AuxPanel = uipanel(app.MainGrid);
            app.AuxPanel.Layout.Row = 2;
            app.AuxPanel.Layout.Column = 2;
            app.AuxPanel.Title = 'Reference';

            % 在Reference面板内创建垂直网格布局 (4行1列，类似QVBoxLayout)
            app.DashboardGrid = uigridlayout(app.AuxPanel, [4, 1]);
            app.DashboardGrid.RowHeight = {'fit', 'fit', 'fit', '1x'};  % 三个按钮区|内容区
            app.DashboardGrid.ColumnWidth = {'1x'};
            app.DashboardGrid.Padding = [10, 10, 10, 10];
            app.DashboardGrid.RowSpacing = 10;

            % Open ICD按键 [1,1] - 放在Reference面板顶部
            app.OpenICDButton = uibutton(app.DashboardGrid, 'push');
            app.OpenICDButton.Layout.Row = 1;
            app.OpenICDButton.Layout.Column = 1;
            app.OpenICDButton.Text = '📊 Open ICD';
            app.OpenICDButton.BackgroundColor = [0.7 0.9 0.7];
            app.OpenICDButton.FontColor = [0 0 0];
            app.OpenICDButton.FontWeight = 'bold';
            app.OpenICDButton.FontSize = 14;
            app.OpenICDButton.Tooltip = '打开ICD Excel文件（根据project_config.yml配置）';

            % 打开系统架构文档按键 [2,1]
            app.OpenSystemArchitectureButton = uibutton(app.DashboardGrid, 'push');
            app.OpenSystemArchitectureButton.Layout.Row = 2;
            app.OpenSystemArchitectureButton.Layout.Column = 1;
            app.OpenSystemArchitectureButton.Text = '🏗️ 打开系统架构文档';
            app.OpenSystemArchitectureButton.BackgroundColor = [0.7 0.8 0.9];
            app.OpenSystemArchitectureButton.FontColor = [0 0 0];
            app.OpenSystemArchitectureButton.FontWeight = 'bold';
            app.OpenSystemArchitectureButton.FontSize = 14;
            app.OpenSystemArchitectureButton.Tooltip = '打开系统架构文档PDF文件（根据project_config.yml配置）';

            % 打开项目文档架构图按键 [3,1]
            app.OpenProjectDocArchitectureButton = uibutton(app.DashboardGrid, 'push');
            app.OpenProjectDocArchitectureButton.Layout.Row = 3;
            app.OpenProjectDocArchitectureButton.Layout.Column = 1;
            app.OpenProjectDocArchitectureButton.Text = '📋 打开项目文档架构图';
            app.OpenProjectDocArchitectureButton.BackgroundColor = [0.9 0.8 0.7];
            app.OpenProjectDocArchitectureButton.FontColor = [0 0 0];
            app.OpenProjectDocArchitectureButton.FontWeight = 'bold';
            app.OpenProjectDocArchitectureButton.FontSize = 14;
            app.OpenProjectDocArchitectureButton.Tooltip = '打开项目文档架构图PDF文件（根据project_config.yml配置）';
        end
        
        function createTabGroup(app)
            % 创建标签页组 - 基于Grid布局，放在主网格的[2,1]位置
            app.TabGroup = uitabgroup(app.MainGrid);
            app.TabGroup.Layout.Row = 2;
            app.TabGroup.Layout.Column = 1;
            % TabGroup会自动占据主网格分配给它的空间，无需手动计算位置和大小
        end
        
        function createRequirementsTab(app)
            % 创建需求管理标签页 - 基于uigridlayout的3列布局（类似QGridLayout）
            app.RequirementsTab = uitab(app.TabGroup);
            app.RequirementsTab.Title = 'Requirements Management';

            % 创建需求管理网格布局 (2行4列)
            app.RequirementsGrid = uigridlayout(app.RequirementsTab, [2, 4]);
            app.RequirementsGrid.RowHeight = {'fit', '1x'};  % 导入按钮行|主面板区
            app.RequirementsGrid.ColumnWidth = {'4x', '1x', '1x', '1x'};  % 4列：导入面板(2x)|添加Tab|删除Tab|View Matrix
            app.RequirementsGrid.Padding = [20, 20, 20, 20];
            app.RequirementsGrid.RowSpacing = 10;
            app.RequirementsGrid.ColumnSpacing = 10;

            % 第1行：导入按钮区域 - 左侧 [1,1]
            % 这里importButtonPanel是占第1列，但"列宽2x"是通过app.RequirementsGrid的ColumnWidth属性来设置的：
            % app.RequirementsGrid.ColumnWidth = {'2x', '1x', '1x', '1x'};
            % 这意味着第1列的宽度是其它列的2倍，设置位置见上一段代码。
            importButtonPanel = uipanel(app.RequirementsGrid);
            importButtonPanel.Layout.Row = 1;
            importButtonPanel.Layout.Column = 1;
            importButtonPanel.Title = 'Requirements Import';
            importButtonPanel.FontWeight = 'bold';

            % 在导入按钮面板内创建水平网格布局
            importButtonGrid = uigridlayout(importButtonPanel, [1, 5]);
            importButtonGrid.RowHeight = {'fit'};
            importButtonGrid.ColumnWidth = {'1x', '1x', '1x', '1x', '1x'};  % 5列：CSV|ReqIF|SLREQX|导出Word|说明
            importButtonGrid.Padding = [10, 5, 10, 5];
            importButtonGrid.ColumnSpacing = 8;

            app.ImportCSVButton = uibutton(importButtonGrid, 'push');
            app.ImportCSVButton.Layout.Row = 1;
            app.ImportCSVButton.Layout.Column = 1;
            app.ImportCSVButton.Text = 'Import CSV';
            app.ImportCSVButton.BackgroundColor = [0.2 0.7 0.3];
            app.ImportCSVButton.FontColor = [1 1 1];
            app.ImportCSVButton.FontWeight = 'bold';

            app.ImportReqIFButton = uibutton(importButtonGrid, 'push');
            app.ImportReqIFButton.Layout.Row = 1;
            app.ImportReqIFButton.Layout.Column = 2;
            app.ImportReqIFButton.Text = 'Import ReqIF';
            app.ImportReqIFButton.BackgroundColor = [0.3 0.5 0.8];
            app.ImportReqIFButton.FontColor = [1 1 1];
            app.ImportReqIFButton.FontWeight = 'bold';

            app.ExportToWordButton = uibutton(importButtonGrid, 'push');
            app.ExportToWordButton.Layout.Row = 1;
            app.ExportToWordButton.Layout.Column = 3;
            app.ExportToWordButton.Text = '导出到Word';
            app.ExportToWordButton.BackgroundColor = [0.6 0.2 0.8];
            app.ExportToWordButton.FontColor = [1 1 1];
            app.ExportToWordButton.FontWeight = 'bold';

            importInfo = uilabel(importButtonGrid);
            importInfo.Layout.Row = 1;
            importInfo.Layout.Column = 5;
            importInfo.Text = '导入/加载/导出需求文件';
            importInfo.FontSize = 8;
            importInfo.HorizontalAlignment = 'center';
            importInfo.VerticalAlignment = 'center';

            % 添加Requirement Tab按钮 [1,2]
            app.AddReqTabButton = uibutton(app.RequirementsGrid, 'push');
            app.AddReqTabButton.Layout.Row = 1;
            app.AddReqTabButton.Layout.Column = 2;
            app.AddReqTabButton.Text = '➕ 添加Tab';
            app.AddReqTabButton.BackgroundColor = [0.2 0.7 0.3];
            app.AddReqTabButton.FontColor = [1 1 1];
            app.AddReqTabButton.FontWeight = 'bold';
            app.AddReqTabButton.Tooltip = '添加新的需求Tab';
            
            % 删除Requirement Tab按钮 [1,3]
            app.DeleteReqTabButton = uibutton(app.RequirementsGrid, 'push');
            app.DeleteReqTabButton.Layout.Row = 1;
            app.DeleteReqTabButton.Layout.Column = 3;
            app.DeleteReqTabButton.Text = '➖ 删除Tab';
            app.DeleteReqTabButton.BackgroundColor = [0.8 0.2 0.2];
            app.DeleteReqTabButton.FontColor = [1 1 1];
            app.DeleteReqTabButton.FontWeight = 'bold';
            app.DeleteReqTabButton.Tooltip = '删除已有的需求Tab';
            
            % View Matrix按钮 [1,4] - 与导入按钮同一行
            app.ViewTraceMatrixButton = uibutton(app.RequirementsGrid, 'push');
            app.ViewTraceMatrixButton.Layout.Row = 1;
            app.ViewTraceMatrixButton.Layout.Column = 4;
            app.ViewTraceMatrixButton.Text = 'View Matrix';
            app.ViewTraceMatrixButton.BackgroundColor = [0.6 0.2 0.8];
            app.ViewTraceMatrixButton.FontColor = [1 1 1];
            app.ViewTraceMatrixButton.FontWeight = 'bold';
            app.ViewTraceMatrixButton.Tooltip = '查看需求追溯矩阵';

            % 主要内容区域（第2行）- 使用1:4比例布局
            mainContentPanel = uipanel(app.RequirementsGrid);
            mainContentPanel.Layout.Row = 2;
            mainContentPanel.Layout.Column = [1, 4];  % 横跨所有4列
            mainContentPanel.BorderType = 'none';
            
            % 创建2列网格布局，比例1:4
            app.ResizableGrid = uigridlayout(mainContentPanel, [1, 3]);
            % 新布局：合并面板 | 分割线 | Status面板 (1:4比例)
            app.ResizableGrid.ColumnWidth = {'1x', 5, '4x'};  % 分割线宽度为5px，Status占4倍空间
            app.ResizableGrid.RowHeight = {'1x'};
            app.ResizableGrid.Padding = [0, 0, 0, 0];
            app.ResizableGrid.ColumnSpacing = 0;
            
            % 第1列：需求文件树面板
            app.ReqListPanel = uipanel(app.ResizableGrid);
            app.ReqListPanel.Layout.Row = 1;
            app.ReqListPanel.Layout.Column = 1;
            app.ReqListPanel.Title = 'Requirements Files';

            % 在面板内创建垂直网格布局
            reqTreeGrid = uigridlayout(app.ReqListPanel, [3, 1]);
            reqTreeGrid.RowHeight = {'fit', '1x', 'fit'};  % 刷新按钮|树控件|打开文件按钮
            reqTreeGrid.ColumnWidth = {'1x'};
            reqTreeGrid.Padding = [10, 10, 10, 10];
            reqTreeGrid.RowSpacing = 5;

            % 刷新按钮 [1,1]
            app.RefreshReqTreeButton = uibutton(reqTreeGrid, 'push');
            app.RefreshReqTreeButton.Layout.Row = 1;
            app.RefreshReqTreeButton.Layout.Column = 1;
            app.RefreshReqTreeButton.Text = '🔄 Refresh Requirements';
            app.RefreshReqTreeButton.BackgroundColor = [0.2 0.6 0.9];
            app.RefreshReqTreeButton.FontColor = [1 1 1];
            app.RefreshReqTreeButton.FontWeight = 'bold';
            app.RefreshReqTreeButton.Tooltip = '刷新需求文件树';

            % 需求文件树 [2,1]
            app.RequirementsTree = uitree(reqTreeGrid);
            app.RequirementsTree.Layout.Row = 2;
            app.RequirementsTree.Layout.Column = 1;
            app.RequirementsTree.Multiselect = 'off';

            % 打开文件按钮 [3,1]
            app.OpenRequirementFileButton = uibutton(reqTreeGrid, 'push');
            app.OpenRequirementFileButton.Layout.Row = 3;
            app.OpenRequirementFileButton.Layout.Column = 1;
            app.OpenRequirementFileButton.Text = '📂 Open Selected Requriement';
            app.OpenRequirementFileButton.BackgroundColor = [0.2 0.8 0.2];
            app.OpenRequirementFileButton.FontColor = [1 1 1];
            app.OpenRequirementFileButton.FontWeight = 'bold';
            app.OpenRequirementFileButton.Tooltip = '打开选中的需求文件';
            app.OpenRequirementFileButton.Enable = 'off'; % 初始状态为禁用

            % 分割线
            app.Divider1 = uipanel(app.ResizableGrid);
            app.Divider1.Layout.Row = 1;
            app.Divider1.Layout.Column = 2;
            app.Divider1.BackgroundColor = [0.8, 0.8, 0.8];
            app.Divider1.BorderType = 'none';
            
            % 添加拖拽提示
            app.divider1Label = uilabel(app.Divider1);
            app.divider1Label.Text = '⋮';
            app.divider1Label.FontSize = 16;
            app.divider1Label.FontWeight = 'bold';
            app.divider1Label.HorizontalAlignment = 'center';
            app.divider1Label.VerticalAlignment = 'center';
            app.divider1Label.Position = [0, 0, 5, 100];
            app.divider1Label.FontColor = [0.5, 0.5, 0.5];
            
            % 第3列：Requirements Status Panel (带子标签页)
            app.ReqStatusPanel = uipanel(app.ResizableGrid);
            app.ReqStatusPanel.Layout.Row = 1;
            app.ReqStatusPanel.Layout.Column = 3;
            app.ReqStatusPanel.Title = 'Requirements Status';

            % 在Status Panel内创建子标签页
            statusGrid = uigridlayout(app.ReqStatusPanel, [1, 1]);
            statusGrid.RowHeight = {'1x'};
            statusGrid.ColumnWidth = {'1x'};
            statusGrid.Padding = [5, 5, 5, 5];

            % 创建子标签组
            app.ReqStatusTabGroup = uitabgroup(statusGrid);
            app.ReqStatusTabGroup.Layout.Row = 1;
            app.ReqStatusTabGroup.Layout.Column = 1;

            % 不再创建固定的HLR和SRS Tab，改为在初始化时动态创建defaultReq Tab
            % 所有Tab（包括defaultReq）都使用统一的动态创建方式，包含加载按钮

            % 设置分割线的点击回调来调整布局
            app.setupResizableLayout();
            
            % Requirements标签页的Grid布局已完成
        end
        
        function createModelDesignAnalysisTab(app)
            % 创建Model Design and Analysis主标签页 - 左右分栏布局（左侧Design Models 30%，右侧二级TabGroup 70%）
            app.ModelDesignAnalysisTab = uitab(app.TabGroup);
            app.ModelDesignAnalysisTab.Title = 'Model Design and Analysis';
            
            % 创建主网格布局 (1行2列：左侧Design Models面板 + 右侧二级TabGroup)
            app.ModelDesignAnalysisGrid = uigridlayout(app.ModelDesignAnalysisTab, [1, 2]);
            app.ModelDesignAnalysisGrid.RowHeight = {'1x'};
            app.ModelDesignAnalysisGrid.ColumnWidth = {'1x', '2.33x'};  % 左侧约30%，右侧约70%
            app.ModelDesignAnalysisGrid.Padding = [10, 10, 10, 10];
            app.ModelDesignAnalysisGrid.ColumnSpacing = 15;
            
            % 左侧 - Design Models 面板 [1,1]
            app.DesignModelsPanel = uipanel(app.ModelDesignAnalysisGrid);
            app.DesignModelsPanel.Layout.Row = 1;
            app.DesignModelsPanel.Layout.Column = 1;
            app.DesignModelsPanel.Title = 'Design Models';
            
            % 在Design Models面板内创建垂直布局
            designModelsGrid = uigridlayout(app.DesignModelsPanel, [2, 1]);
            designModelsGrid.RowHeight = {'1x', 'fit'};  % 树占主要空间，按钮行固定高度
            designModelsGrid.ColumnWidth = {'1x'};
            designModelsGrid.Padding = [10, 10, 10, 10];
            designModelsGrid.RowSpacing = 10;
            
            % 模型树 [1,1]
            app.ModelTree = uitree(designModelsGrid);
            app.ModelTree.Layout.Row = 1;
            app.ModelTree.Layout.Column = 1;
            app.ModelTree.Multiselect = 'on';
            
            % 创建根节点
            app.DesignModelsNode = uitreenode(app.ModelTree);
            app.DesignModelsNode.Text = 'Design Models';
            
            % 刷新树按钮 [2,1]
            app.RefreshTreeButton = uibutton(designModelsGrid, 'push');
            app.RefreshTreeButton.Layout.Row = 2;
            app.RefreshTreeButton.Layout.Column = 1;
            app.RefreshTreeButton.Text = '🔄 Refresh Tree';
            app.RefreshTreeButton.FontSize = 10;
            
            % 右侧 - 二级TabGroup面板 [1,2]
            app.SubTabPanel = uipanel(app.ModelDesignAnalysisGrid);
            app.SubTabPanel.Layout.Row = 1;
            app.SubTabPanel.Layout.Column = 2;
            app.SubTabPanel.Title = '';
            app.SubTabPanel.BorderType = 'none';
            
            % 在面板内创建二级TabGroup
            subTabGrid = uigridlayout(app.SubTabPanel, [1, 1]);
            subTabGrid.RowHeight = {'1x'};
            subTabGrid.ColumnWidth = {'1x'};
            subTabGrid.Padding = [0, 0, 0, 0];
            
            app.SubTabGroup = uitabgroup(subTabGrid);
            app.SubTabGroup.Layout.Row = 1;
            app.SubTabGroup.Layout.Column = 1;
            
            % 创建二级标签页
            app.createModelManagementSubTab();
            app.createModelAnalysisSubTab();
            app.createCodeAnalysisSubTab();
            app.createTestingManagementSubTab();
            
            % 设置默认选中Model Management标签页
            app.SubTabGroup.SelectedTab = app.ModelManagementTab;
        end
        
        function createModelManagementSubTab(app)
            % 创建模型管理二级标签页 - 基于uigridlayout的3行2列布局（第一列：操作+依赖+删除按钮，第二列：创建面板）
            app.ModelManagementTab = uitab(app.SubTabGroup);
            app.ModelManagementTab.Title = 'Model Management';
            
            % 创建模型管理网格布局 (3行2列) - 第一列为功能面板，第二列为创建面板
            app.ModelGrid = uigridlayout(app.ModelManagementTab, [3, 2]);
            app.ModelGrid.RowHeight = {'1x', '1x', 'fit'};    % 操作面板 + 依赖面板 + 删除按钮
            app.ModelGrid.ColumnWidth = {'1x', '1x'};         % 两列等宽
            app.ModelGrid.Padding = [20, 20, 20, 20];
            app.ModelGrid.RowSpacing = 15;
            app.ModelGrid.ColumnSpacing = 15;

            % 左上 - 模型操作面板 [1,1] 
            app.ModelActionsPanel = uipanel(app.ModelGrid);
            app.ModelActionsPanel.Layout.Row = 1;
            app.ModelActionsPanel.Layout.Column = 1;
            app.ModelActionsPanel.Title = 'Model Operations';
            
            % 在操作面板内创建网格布局 - 改为上下布局（2行1列）
            actionsGrid = uigridlayout(app.ModelActionsPanel, [2, 1]);
            actionsGrid.RowHeight = {'fit', 'fit'};          % 2行按钮，自适应高度
            actionsGrid.ColumnWidth = {'1x'};               % 1列自适应宽度
            actionsGrid.Padding = [10, 10, 10, 10];
            actionsGrid.RowSpacing = 10;
            actionsGrid.ColumnSpacing = 10;
            
            % 操作按钮 - 上下布局
            app.SelectModelButton = uibutton(actionsGrid, 'push');
            app.SelectModelButton.Layout.Row = 1;
            app.SelectModelButton.Layout.Column = 1;
            app.SelectModelButton.Text = 'Select Model';
            app.SelectModelButton.BackgroundColor = [0.2 0.7 0.2];
            app.SelectModelButton.FontColor = [1 1 1];
            app.SelectModelButton.FontWeight = 'bold';
            app.SelectModelButton.Tooltip = '从树中选择模型并设为当前模型';
            
            app.OpenModelButton = uibutton(actionsGrid, 'push');
            app.OpenModelButton.Layout.Row = 2;              % 移到第2行
            app.OpenModelButton.Layout.Column = 1;
            app.OpenModelButton.Text = 'Open Current Model';
            app.OpenModelButton.BackgroundColor = [0.2 0.6 0.9];
            app.OpenModelButton.FontColor = [1 1 1];
            app.OpenModelButton.FontWeight = 'bold';
            app.OpenModelButton.Tooltip = '打开选中的模型文件';

            % 右上 - Model Creation Panel [1,2] 
            app.ModelCreationPanel = uipanel(app.ModelGrid);
            app.ModelCreationPanel.Layout.Row = 1;         % 只占第1行
            app.ModelCreationPanel.Layout.Column = 2;
            app.ModelCreationPanel.Title = 'Model Creation & Setup';
            
            % 在Creation Panel内创建垂直网格布局
            creationGrid = uigridlayout(app.ModelCreationPanel, [3, 1]);
            creationGrid.RowHeight = {'fit', 'fit', 'fit'};  % 3行控件，自适应高度
            creationGrid.ColumnWidth = {'1x'};        % 1列自适应
            creationGrid.Padding = [10, 10, 10, 10];
            creationGrid.RowSpacing = 10;
            
            % 第1行：模板选择下拉框
            app.ModelTemplateDropDown = uidropdown(creationGrid);
            app.ModelTemplateDropDown.Layout.Row = 1;
            app.ModelTemplateDropDown.Layout.Column = 1;
            app.ModelTemplateDropDown.Items = {'Multi-Instance', 'Single-Instance'};
            app.ModelTemplateDropDown.Value = 'Single-Instance';
            
            % 第2行：新建模型按钮
            app.NewModelButton = uibutton(creationGrid, 'push');
            app.NewModelButton.Layout.Row = 2;
            app.NewModelButton.Layout.Column = 1;
            app.NewModelButton.Text = 'New Model';
            app.NewModelButton.BackgroundColor = [0.2 0.6 0.9];
            app.NewModelButton.FontColor = [1 1 1];
            app.NewModelButton.FontWeight = 'bold';
            
            % 第3行：导入模型按钮
            app.ImportModelButton = uibutton(creationGrid, 'push');
            app.ImportModelButton.Layout.Row = 3;
            app.ImportModelButton.Layout.Column = 1;
            app.ImportModelButton.Text = 'Import Model';
            app.ImportModelButton.BackgroundColor = [0.6 0.7 0.2];
            app.ImportModelButton.FontColor = [1 1 1];
            app.ImportModelButton.FontWeight = 'bold';

            % 右下 - Model Dashboard Panel [2-3,2]
            app.ModelDashboardPanel = uipanel(app.ModelGrid);
            app.ModelDashboardPanel.Layout.Row = [2, 3];    % 跨越第2-3行
            app.ModelDashboardPanel.Layout.Column = 2;
            app.ModelDashboardPanel.Title = 'Model Dashboard';
            
            % 在Dashboard Panel内创建垂直网格布局
            dashboardGrid = uigridlayout(app.ModelDashboardPanel, [2, 1]);
            dashboardGrid.RowHeight = {'fit', 'fit'};        % 2行按钮，自适应高度
            dashboardGrid.ColumnWidth = {'1x'};              % 1列自适应
            dashboardGrid.Padding = [10, 10, 10, 10];
            dashboardGrid.RowSpacing = 10;
            
            % 第1行：刷新模型仪表盘按钮
            app.RefreshModelDashboardButton = uibutton(dashboardGrid, 'push');
            app.RefreshModelDashboardButton.Layout.Row = 1;
            app.RefreshModelDashboardButton.Layout.Column = 1;
            app.RefreshModelDashboardButton.Text = 'Refresh Current Model Dashboard';
            app.RefreshModelDashboardButton.BackgroundColor = [0.8 0.4 0.2];
            app.RefreshModelDashboardButton.FontColor = [1 1 1];
            app.RefreshModelDashboardButton.FontWeight = 'bold';
            
            % 第2行：打开模型仪表盘按钮
            app.OpenModelDashboardButton = uibutton(dashboardGrid, 'push');
            app.OpenModelDashboardButton.Layout.Row = 2;
            app.OpenModelDashboardButton.Layout.Column = 1;
            app.OpenModelDashboardButton.Text = 'Open Current Model Dashboard';
            app.OpenModelDashboardButton.BackgroundColor = [0.3 0.6 0.8];
            app.OpenModelDashboardButton.FontColor = [1 1 1];
            app.OpenModelDashboardButton.FontWeight = 'bold';

            % 第一列中部 - 依赖分析和数据管理 [2,1]
            app.ModelDependencyPanel = uipanel(app.ModelGrid);
            app.ModelDependencyPanel.Layout.Row = 2;
            app.ModelDependencyPanel.Layout.Column = 1;      % 只在第一列
            app.ModelDependencyPanel.Title = 'Dependencies & Data Management';
            
            % 改为纵向布局：3行1列
            dependencyGrid = uigridlayout(app.ModelDependencyPanel, [3, 1]);
            dependencyGrid.RowHeight = {'fit', 'fit', 'fit'}; % 3行按钮，自适应高度
            dependencyGrid.ColumnWidth = {'1x'};              % 1列自适应宽度
            dependencyGrid.Padding = [10, 10, 10, 10];
            dependencyGrid.RowSpacing = 10;
            
            app.ViewModelDependencyButton = uibutton(dependencyGrid, 'push');
            app.ViewModelDependencyButton.Layout.Row = 1;
            app.ViewModelDependencyButton.Layout.Column = 1;
            app.ViewModelDependencyButton.Text = 'View Current Model Dependencies';
            app.ViewModelDependencyButton.BackgroundColor = [0.7 0.7 0.2];
            app.ViewModelDependencyButton.FontColor = [1 1 1];
            app.ViewModelDependencyButton.FontWeight = 'bold';
            
            app.ViewFileDependencyButton = uibutton(dependencyGrid, 'push');
            app.ViewFileDependencyButton.Layout.Row = 2;     % 移到第2行
            app.ViewFileDependencyButton.Layout.Column = 1;
            app.ViewFileDependencyButton.Text = 'View Current Model File Dependencies';
            app.ViewFileDependencyButton.BackgroundColor = [0.7 0.7 0.2];
            app.ViewFileDependencyButton.FontColor = [1 1 1];
            app.ViewFileDependencyButton.FontWeight = 'bold';
            
            app.OpenSLDDButton = uibutton(dependencyGrid, 'push');
            app.OpenSLDDButton.Layout.Row = 3;               % 移到第3行
            app.OpenSLDDButton.Layout.Column = 1;
            app.OpenSLDDButton.Text = 'Open Current Model SLDD';
            app.OpenSLDDButton.BackgroundColor = [0.8 0.5 0.9];
            app.OpenSLDDButton.FontColor = [1 1 1];
            app.OpenSLDDButton.FontWeight = 'bold';
            
            % 第一列底部 - 单独的删除模型按钮 [3,1]
            app.DeleteModelButton = uibutton(app.ModelGrid, 'push');
            app.DeleteModelButton.Layout.Row = 3;
            app.DeleteModelButton.Layout.Column = 1;
            app.DeleteModelButton.Text = '❌ Delete Selected Model(s)';
            app.DeleteModelButton.BackgroundColor = [0.9 0.2 0.2];
            app.DeleteModelButton.FontColor = [1 1 1];
            app.DeleteModelButton.FontWeight = 'bold';
            app.DeleteModelButton.Tooltip = '删除选中的模型文件（支持多选）';
        end
        function createModelAnalysisSubTab(app)
            % 创建模型分析二级标签页 - 集中所有分析和报告功能
            app.ModelAnalysisTab = uitab(app.SubTabGroup);
            app.ModelAnalysisTab.Title = 'Model Analysis';
            
            % 创建模型分析网格布局 (4行2列：顶部RunAll按钮 + 3行分析面板)
            app.ModelAnalysisGrid = uigridlayout(app.ModelAnalysisTab, [4, 2]);
            app.ModelAnalysisGrid.RowHeight = {'fit', '1x', '1x', '1x'};  % RunAll按钮行|第一行面板|第二行面板|第三行面板
            app.ModelAnalysisGrid.ColumnWidth = {'1x', '1x'}; % 2列等宽
            app.ModelAnalysisGrid.Padding = [20, 20, 20, 20];
            app.ModelAnalysisGrid.RowSpacing = 15;
            app.ModelAnalysisGrid.ColumnSpacing = 15;
            
            % [1,1-2] Run All Button - 横跨两列的顶部通用按钮
            app.RunAllButton = uibutton(app.ModelAnalysisGrid, 'push');
            app.RunAllButton.Layout.Row = 1;
            app.RunAllButton.Layout.Column = [1, 2];  % 横跨两列
            app.RunAllButton.Text = '🚀 Run All Model Analysis';
            app.RunAllButton.BackgroundColor = [0.2 0.8 0.2];
            app.RunAllButton.FontColor = [1 1 1];
            app.RunAllButton.FontWeight = 'bold';
            app.RunAllButton.FontSize = 14;
            app.RunAllButton.Tooltip = '依次执行所有模型分析：Model Advisor → SDD报告 → 模型指标 → SLDV检测';
            
            % [2,1] Model Standards Panel - Model Advisor功能
            app.ModelStandardsPanel = uipanel(app.ModelAnalysisGrid);
            app.ModelStandardsPanel.Layout.Row = 2;
            app.ModelStandardsPanel.Layout.Column = 1;
            app.ModelStandardsPanel.Title = 'Model Standards Check';

            % 在Model Standards Panel内创建网格布局 - 3行2列，第1行生成报告+状态灯，其他按钮纵向排列
            standardsGrid = uigridlayout(app.ModelStandardsPanel, [3, 2]);
            standardsGrid.RowHeight = {'fit', 'fit', 'fit'};
            standardsGrid.ColumnWidth = {'1x', 'fit'};
            standardsGrid.Padding = [10, 10, 10, 10];
            standardsGrid.RowSpacing = 10;
            standardsGrid.ColumnSpacing = 10;
            
            % 第1行：生成报告按钮 + 状态灯在右边
            app.RunModelAdvisorButton = uibutton(standardsGrid, 'push');
            app.RunModelAdvisorButton.Layout.Row = 1;
            app.RunModelAdvisorButton.Layout.Column = 1;
            app.RunModelAdvisorButton.Text = 'Generate Model Advisor report';
            app.RunModelAdvisorButton.BackgroundColor = [0.8 0.4 0.2];
            app.RunModelAdvisorButton.FontColor = [1 1 1];
            app.RunModelAdvisorButton.FontWeight = 'bold';

            app.ModelAdvisorLamp = uilamp(standardsGrid);
            app.ModelAdvisorLamp.Layout.Row = 1;
            app.ModelAdvisorLamp.Layout.Column = 2;
            app.ModelAdvisorLamp.Color = [0.8 0.8 0.8]; % 默认灰色
            
            % 第2行：Open Model Advisor按钮
            app.OpenModelAdvisorButton = uibutton(standardsGrid, 'push');
            app.OpenModelAdvisorButton.Layout.Row = 2;
            app.OpenModelAdvisorButton.Layout.Column = [1, 2]; % 横跨两列
            app.OpenModelAdvisorButton.Text = 'Open Model Advisor';
            app.OpenModelAdvisorButton.BackgroundColor = [0.9 0.9 0.9];
            
            % 第3行：Open Report按钮  
            app.OpenReportButton = uibutton(standardsGrid, 'push');
            app.OpenReportButton.Layout.Row = 3;
            app.OpenReportButton.Layout.Column = [1, 2]; % 横跨两列
            app.OpenReportButton.Text = 'Open Report';
            app.OpenReportButton.BackgroundColor = [0.9 0.9 0.9];
            
            % [2,2] Model Reports Panel - SDD报告功能  
            app.ModelReportsPanel = uipanel(app.ModelAnalysisGrid);
            app.ModelReportsPanel.Layout.Row = 2;
            app.ModelReportsPanel.Layout.Column = 2;
            app.ModelReportsPanel.Title = 'Model Reports (SDD)';
            
            reportsGrid = uigridlayout(app.ModelReportsPanel, [2, 2]);
            reportsGrid.RowHeight = {'fit', 'fit'};
            reportsGrid.ColumnWidth = {'1x', 'fit'};
            reportsGrid.Padding = [10, 10, 10, 10];
            reportsGrid.RowSpacing = 10;
            reportsGrid.ColumnSpacing = 10;
            
            % 第1行：生成报告按钮 + 状态灯在右边
            app.SDDReportButton = uibutton(reportsGrid, 'push');
            app.SDDReportButton.Layout.Row = 1;
            app.SDDReportButton.Layout.Column = 1;
            app.SDDReportButton.Text = 'Generate SDD';
            app.SDDReportButton.BackgroundColor = [0.2 0.6 0.9];
            app.SDDReportButton.FontColor = [1 1 1];
            app.SDDReportButton.FontWeight = 'bold';
            
            app.SDDLamp = uilamp(reportsGrid);
            app.SDDLamp.Layout.Row = 1;
            app.SDDLamp.Layout.Column = 2;
            app.SDDLamp.Color = [0.8 0.8 0.8];
            
            % 第2行：Open SDD按钮
            app.OpenSDDButton = uibutton(reportsGrid, 'push');
            app.OpenSDDButton.Layout.Row = 2;
            app.OpenSDDButton.Layout.Column = [1, 2];  % 横跨两列
            app.OpenSDDButton.Text = 'Open SDD';
            app.OpenSDDButton.BackgroundColor = [0.9 0.9 0.9];
            
            % [3,1] Model Metrics Panel
            app.ModelMetricsPanel = uipanel(app.ModelAnalysisGrid);
            app.ModelMetricsPanel.Layout.Row = 3;
            app.ModelMetricsPanel.Layout.Column = 1;
            app.ModelMetricsPanel.Title = 'Model Metrics';

            % 在Model Metrics Panel内创建网格布局
            metricsGrid = uigridlayout(app.ModelMetricsPanel, [2, 2]);
            metricsGrid.RowHeight = {'fit', 'fit'};
            metricsGrid.ColumnWidth = {'1x', 'fit'};
            metricsGrid.Padding = [10, 10, 10, 10];
            metricsGrid.RowSpacing = 10;
            metricsGrid.ColumnSpacing = 10;

            % 第1行：生成报告按钮 + 状态灯在右边
            app.ComputeModelMetricsButton = uibutton(metricsGrid, 'push');
            app.ComputeModelMetricsButton.Layout.Row = 1;
            app.ComputeModelMetricsButton.Layout.Column = 1;
            app.ComputeModelMetricsButton.Text = 'Generate Model Metrics Report';
            app.ComputeModelMetricsButton.BackgroundColor = [0.6 0.2 0.8];
            app.ComputeModelMetricsButton.FontColor = [1 1 1];
            app.ComputeModelMetricsButton.FontWeight = 'bold';

            app.ModelMetricsLamp = uilamp(metricsGrid);
            app.ModelMetricsLamp.Layout.Row = 1;
            app.ModelMetricsLamp.Layout.Column = 2;
            app.ModelMetricsLamp.Color = [0.8 0.8 0.8]; % 默认灰色
            
            % 第2行：Open Report按钮
            app.ModelMetricsOpenReportButton = uibutton(metricsGrid, 'push');
            app.ModelMetricsOpenReportButton.Layout.Row = 2;
            app.ModelMetricsOpenReportButton.Layout.Column = [1, 2];  % 横跨两列
            app.ModelMetricsOpenReportButton.Text = 'Open Report';
            app.ModelMetricsOpenReportButton.BackgroundColor = [0.9 0.9 0.9];
            
            % [3,2] Design Error Detection Panel (SLDV)
            app.SLDVPanel = uipanel(app.ModelAnalysisGrid);
            app.SLDVPanel.Layout.Row = 3;
            app.SLDVPanel.Layout.Column = 2;
            app.SLDVPanel.Title = 'Design Error Detection (SLDV)';
            
            % 在SLDV Panel内创建网格布局 - 3行2列：生成报告+灯，其他按钮纵向排列
            sldvGrid = uigridlayout(app.SLDVPanel, [3, 2]);
            sldvGrid.RowHeight = {'fit', 'fit', 'fit'};
            sldvGrid.ColumnWidth = {'1x', 'fit'};
            sldvGrid.Padding = [10, 10, 10, 10];
            sldvGrid.RowSpacing = 10;
            sldvGrid.ColumnSpacing = 10;
            
            % 第1行：生成报告按钮 + 状态灯在右边
            app.RunSLDVButton = uibutton(sldvGrid, 'push');
            app.RunSLDVButton.Layout.Row = 1;
            app.RunSLDVButton.Layout.Column = 1;
            app.RunSLDVButton.Text = 'Generate SLDV Report';
            app.RunSLDVButton.BackgroundColor = [0.3 0.7 0.4];
            app.RunSLDVButton.FontColor = [1 1 1];
            app.RunSLDVButton.FontWeight = 'bold';

            app.SLDVLamp = uilamp(sldvGrid);
            app.SLDVLamp.Layout.Row = 1;
            app.SLDVLamp.Layout.Column = 2;
            app.SLDVLamp.Color = [0.8 0.8 0.8]; % 默认灰色
            
            % 第2行：Open Report按钮
            app.SLDVOpenReportButton = uibutton(sldvGrid, 'push');
            app.SLDVOpenReportButton.Layout.Row = 2;
            app.SLDVOpenReportButton.Layout.Column = [1, 2]; % 横跨两列
            app.SLDVOpenReportButton.Text = 'Open Report';
            app.SLDVOpenReportButton.BackgroundColor = [0.9 0.9 0.9];
            
            % 第3行：Open SLDV按钮
            app.OpenSLDVButton = uibutton(sldvGrid, 'push');
            app.OpenSLDVButton.Layout.Row = 3;
            app.OpenSLDVButton.Layout.Column = [1, 2]; % 横跨两列
            app.OpenSLDVButton.Text = 'Open SLDV';
            app.OpenSLDVButton.BackgroundColor = [0.9 0.9 0.9];
            
            % [4,1] Model Checklist Panel - 模型检查清单
            app.ModelChecklistPanel = uipanel(app.ModelAnalysisGrid);
            app.ModelChecklistPanel.Layout.Row = 4;
            app.ModelChecklistPanel.Layout.Column = 1;
            app.ModelChecklistPanel.Title = 'Model Checklist';

            % 在Model Checklist Panel内创建网格布局 - 2行2列：按钮纵向排列
            checklistGrid = uigridlayout(app.ModelChecklistPanel, [2, 2]);
            checklistGrid.RowHeight = {'fit', 'fit'};
            checklistGrid.ColumnWidth = {'1x', 'fit'};
            checklistGrid.Padding = [10, 10, 10, 10];
            checklistGrid.RowSpacing = 10;
            checklistGrid.ColumnSpacing = 10;
            
            % 第1行：新建模型检查清单按钮 + 状态灯
            app.CreateModelChecklistButton = uibutton(checklistGrid, 'push');
            app.CreateModelChecklistButton.Layout.Row = 1;
            app.CreateModelChecklistButton.Layout.Column = 1;
            app.CreateModelChecklistButton.Text = 'Create Model Checklist';
            app.CreateModelChecklistButton.BackgroundColor = [0.2 0.5 0.8];
            app.CreateModelChecklistButton.FontColor = [1 1 1];
            app.CreateModelChecklistButton.FontWeight = 'bold';

            app.ModelChecklistLamp = uilamp(checklistGrid);
            app.ModelChecklistLamp.Layout.Row = 1;
            app.ModelChecklistLamp.Layout.Column = 2;
            app.ModelChecklistLamp.Color = [0.8 0.8 0.8]; % 默认灰色
            
            % 第2行：打开模型检查清单按钮
            app.OpenModelChecklistButton = uibutton(checklistGrid, 'push');
            app.OpenModelChecklistButton.Layout.Row = 2;
            app.OpenModelChecklistButton.Layout.Column = [1, 2]; % 横跨两列
            app.OpenModelChecklistButton.Text = 'Open Model Checklist';
            app.OpenModelChecklistButton.BackgroundColor = [0.9 0.9 0.9];
        end
        
        function createCodeAnalysisSubTab(app)
            % 创建代码分析二级标签页 - 基于uigridlayout的3x2矩阵布局
            app.CodeGenerationTab = uitab(app.SubTabGroup);
            app.CodeGenerationTab.Title = 'Code Analysis';
            
            % 创建代码分析网格布局 (3行2列：顶部RunAll按钮 + 2行分析面板)
            app.CodeGrid = uigridlayout(app.CodeGenerationTab, [3, 2]);
            app.CodeGrid.RowHeight = {'fit', '1x', '1x'};  % RunAll按钮行|第一行面板|第二行面板
            app.CodeGrid.ColumnWidth = {'1x', '1x'};     % 2列等宽
            app.CodeGrid.Padding = [20, 20, 20, 20];
            app.CodeGrid.RowSpacing = 15;
            app.CodeGrid.ColumnSpacing = 15;
            
            % [1,1-2] Run All Button - 横跨两列的顶部通用按钮
            app.RunAllCodeAnalysisButton = uibutton(app.CodeGrid, 'push');
            app.RunAllCodeAnalysisButton.Layout.Row = 1;
            app.RunAllCodeAnalysisButton.Layout.Column = [1, 2];  % 横跨两列
            app.RunAllCodeAnalysisButton.Text = '🔍 Run All Code Analysis';
            app.RunAllCodeAnalysisButton.BackgroundColor = [0.2 0.8 0.2];
            app.RunAllCodeAnalysisButton.FontColor = [1 1 1];
            app.RunAllCodeAnalysisButton.FontWeight = 'bold';
            app.RunAllCodeAnalysisButton.FontSize = 14;
            app.RunAllCodeAnalysisButton.Tooltip = '依次执行所有代码分析：代码生成 → 代码检查 → Polyspace分析 → 代码指标计算';

            % [2,1] Code Generation Panel
            app.CodeGenPanel = uipanel(app.CodeGrid);
            app.CodeGenPanel.Layout.Row = 2;
            app.CodeGenPanel.Layout.Column = 1;
            app.CodeGenPanel.Title = 'Code Generation';

            % 在Code Gen Panel内创建垂直布局
            codeGenGrid = uigridlayout(app.CodeGenPanel, [3, 2]);
            codeGenGrid.RowHeight = {'fit', 'fit', 'fit'};  % 弹性空间|按钮|状态
            codeGenGrid.ColumnWidth = {'1x', 'fit'};
            codeGenGrid.Padding = [10, 10, 10, 10];
            codeGenGrid.RowSpacing = 5;
            codeGenGrid.ColumnSpacing = 10;

            app.GenerateCodeButton = uibutton(codeGenGrid, 'push');
            app.GenerateCodeButton.Layout.Row = 2;
            app.GenerateCodeButton.Layout.Column = 1;
            app.GenerateCodeButton.Text = 'Generate Code';
            app.GenerateCodeButton.BackgroundColor = [0.2 0.7 0.3];
            app.GenerateCodeButton.FontColor = [1 1 1];
            app.GenerateCodeButton.FontWeight = 'bold';

            app.CodeGenLamp = uilamp(codeGenGrid);
            app.CodeGenLamp.Layout.Row = 2;
            app.CodeGenLamp.Layout.Column = 2;
            
            % 添加打开代码生成报告按钮
            app.OpenCodeReportButton = uibutton(codeGenGrid, 'push');
            app.OpenCodeReportButton.Layout.Row = 3;
            app.OpenCodeReportButton.Layout.Column = 1;
            app.OpenCodeReportButton.Text = 'Open Report';
            app.OpenCodeReportButton.BackgroundColor = [0.5 0.5 0.5];
            app.OpenCodeReportButton.FontColor = [1 1 1];

            % [2,2] Code Inspection Panel
            app.CodeInspectionPanel = uipanel(app.CodeGrid);
            app.CodeInspectionPanel.Layout.Row = 2;
            app.CodeInspectionPanel.Layout.Column = 2;
            app.CodeInspectionPanel.Title = 'Code Inspection';

            % 在Code Inspection Panel内创建垂直布局
            inspectionGrid = uigridlayout(app.CodeInspectionPanel, [4, 2]);
            inspectionGrid.RowHeight = {'fit', 'fit', 'fit', 'fit'};
            inspectionGrid.ColumnWidth = {'1x', 'fit'};
            inspectionGrid.Padding = [10, 10, 10, 10];
            inspectionGrid.RowSpacing = 5;
            inspectionGrid.ColumnSpacing = 10;

            app.RunCodeInspectorButton = uibutton(inspectionGrid, 'push');
            app.RunCodeInspectorButton.Layout.Row = 2;
            app.RunCodeInspectorButton.Layout.Column = 1;
            app.RunCodeInspectorButton.Text = 'Run Code Inspector';
            app.RunCodeInspectorButton.BackgroundColor = [0.3 0.5 0.8];
            app.RunCodeInspectorButton.FontColor = [1 1 1];
            app.RunCodeInspectorButton.FontWeight = 'bold';

            app.CodeInspectorLamp = uilamp(inspectionGrid);
            app.CodeInspectorLamp.Layout.Row = 2;
            app.CodeInspectorLamp.Layout.Column = 2;
            
            % 添加 Open Report 按钮
            app.OpenCodeInspectorReportButton = uibutton(inspectionGrid, 'push');
            app.OpenCodeInspectorReportButton.Layout.Row = 3;
            app.OpenCodeInspectorReportButton.Layout.Column = [1, 2];  % 横跨两列
            app.OpenCodeInspectorReportButton.Text = 'Open Report';
            app.OpenCodeInspectorReportButton.BackgroundColor = [0.5 0.5 0.5];
            app.OpenCodeInspectorReportButton.FontColor = [1 1 1];

            % [3,1] Code Analysis Panel
            app.CodeAnalysisPanel = uipanel(app.CodeGrid);
            app.CodeAnalysisPanel.Layout.Row = 3;
            app.CodeAnalysisPanel.Layout.Column = 1;
            app.CodeAnalysisPanel.Title = 'Static Analysis (Polyspace)';

            % 在Code Analysis Panel内创建垂直布局 - 3个按钮 + 3个Open Report按钮
            analysisGrid = uigridlayout(app.CodeAnalysisPanel, [7, 2]);
            analysisGrid.RowHeight = {'fit', 'fit', 'fit', 'fit', 'fit', 'fit', 'fit'}; 
            analysisGrid.ColumnWidth = {'1x', 'fit'};
            analysisGrid.Padding = [10, 10, 10, 10];
            analysisGrid.RowSpacing = 5;
            analysisGrid.ColumnSpacing = 10;

            % Bug Finder按钮
            app.RunBugFinderButton = uibutton(analysisGrid, 'push');
            app.RunBugFinderButton.Layout.Row = 1;
            app.RunBugFinderButton.Layout.Column = 1;
            app.RunBugFinderButton.Text = 'Bug Finder';
            app.RunBugFinderButton.BackgroundColor = [0.8 0.3 0.3];
            app.RunBugFinderButton.FontColor = [1 1 1];
            app.RunBugFinderButton.FontWeight = 'bold';
            app.RunBugFinderButton.Tooltip = 'Polyspace Bug Finder - 检测编码错误';

            app.BugFinderLamp = uilamp(analysisGrid);
            app.BugFinderLamp.Layout.Row = 1;
            app.BugFinderLamp.Layout.Column = 2;
            
            % Open Bug Finder Report 按钮
            app.OpenBugFinderReportButton = uibutton(analysisGrid, 'push');
            app.OpenBugFinderReportButton.Layout.Row = 2;
            app.OpenBugFinderReportButton.Layout.Column = [1, 2];  % 横跨两列
            app.OpenBugFinderReportButton.Text = 'Open Report';
            app.OpenBugFinderReportButton.BackgroundColor = [0.5 0.5 0.5];
            app.OpenBugFinderReportButton.FontColor = [1 1 1];

            % Code Standards按钮
            app.RunCodeStandardsButton = uibutton(analysisGrid, 'push');
            app.RunCodeStandardsButton.Layout.Row = 3;
            app.RunCodeStandardsButton.Layout.Column = 1;
            app.RunCodeStandardsButton.Text = 'Code Standards';
            app.RunCodeStandardsButton.BackgroundColor = [0.6 0.4 0.2];
            app.RunCodeStandardsButton.FontColor = [1 1 1];
            app.RunCodeStandardsButton.FontWeight = 'bold';
            app.RunCodeStandardsButton.Tooltip = 'Polyspace Code Standards - MISRA C检查';

            app.CodeStandardsLamp = uilamp(analysisGrid);
            app.CodeStandardsLamp.Layout.Row = 3;
            app.CodeStandardsLamp.Layout.Column = 2;
            
            % Open Code Standards Report 按钮
            app.OpenCodeStandardsReportButton = uibutton(analysisGrid, 'push');
            app.OpenCodeStandardsReportButton.Layout.Row = 4;
            app.OpenCodeStandardsReportButton.Layout.Column = [1, 2];  % 横跨两列
            app.OpenCodeStandardsReportButton.Text = 'Open Report';
            app.OpenCodeStandardsReportButton.BackgroundColor = [0.5 0.5 0.5];
            app.OpenCodeStandardsReportButton.FontColor = [1 1 1];

            % Code Prover按钮
            app.RunCodeProverButton = uibutton(analysisGrid, 'push');
            app.RunCodeProverButton.Layout.Row = 5;
            app.RunCodeProverButton.Layout.Column = 1;
            app.RunCodeProverButton.Text = 'Code Prover';
            app.RunCodeProverButton.BackgroundColor = [0.4 0.3 0.6];
            app.RunCodeProverButton.FontColor = [1 1 1];
            app.RunCodeProverButton.FontWeight = 'bold';
            app.RunCodeProverButton.Tooltip = 'Polyspace Code Prover - 证明代码质量';

            app.CodeProverLamp = uilamp(analysisGrid);
            app.CodeProverLamp.Layout.Row = 5;
            app.CodeProverLamp.Layout.Column = 2;
            
            % Open Code Prover Report 按钮
            app.OpenCodeProverReportButton = uibutton(analysisGrid, 'push');
            app.OpenCodeProverReportButton.Layout.Row = 6;
            app.OpenCodeProverReportButton.Layout.Column = [1, 2];  % 横跨两列
            app.OpenCodeProverReportButton.Text = 'Open Report';
            app.OpenCodeProverReportButton.BackgroundColor = [0.5 0.5 0.5];
            app.OpenCodeProverReportButton.FontColor = [1 1 1];


            % [3,2] Code Quality Panel
            app.CodeQualityPanel = uipanel(app.CodeGrid);
            app.CodeQualityPanel.Layout.Row = 3;
            app.CodeQualityPanel.Layout.Column = 2;
            app.CodeQualityPanel.Title = 'Code Quality Metrics';

            % 在Code Quality Panel内创建垂直布局
            qualityGrid = uigridlayout(app.CodeQualityPanel, [5, 2]);
            qualityGrid.RowHeight = {'fit', 'fit', 'fit', 'fit', 'fit'};
            qualityGrid.ColumnWidth = {'1x', 'fit'};
            qualityGrid.Padding = [10, 10, 10, 10];
            qualityGrid.RowSpacing = 5;
            qualityGrid.ColumnSpacing = 10;

            app.ComputeCodeMetricsButton = uibutton(qualityGrid, 'push');
            app.ComputeCodeMetricsButton.Layout.Row = 2;
            app.ComputeCodeMetricsButton.Layout.Column = 1;
            app.ComputeCodeMetricsButton.Text = 'Compute Code Metrics';
            app.ComputeCodeMetricsButton.BackgroundColor = [0.7 0.4 0.7];
            app.ComputeCodeMetricsButton.FontColor = [1 1 1];
            app.ComputeCodeMetricsButton.FontWeight = 'bold';

            app.CodeMetricsLamp = uilamp(qualityGrid);
            app.CodeMetricsLamp.Layout.Row = 2;
            app.CodeMetricsLamp.Layout.Column = 2;
            
            % 添加 Open Report 按钮
            app.OpenCodeMetricsReportButton = uibutton(qualityGrid, 'push');
            app.OpenCodeMetricsReportButton.Layout.Row = 3;
            app.OpenCodeMetricsReportButton.Layout.Column = [1, 2];  % 横跨两列
            app.OpenCodeMetricsReportButton.Text = 'Open Report';
            app.OpenCodeMetricsReportButton.BackgroundColor = [0.5 0.5 0.5];
            app.OpenCodeMetricsReportButton.FontColor = [1 1 1];

        end
        
        function createTestingManagementSubTab(app)
            % 创建测试管理二级标签页 - 基于uigridlayout的复合布局（顶部按钮行 + 中部4列面板 + 底部表格）
            app.TestingTab = uitab(app.SubTabGroup);
            app.TestingTab.Title = 'Testing Management';

            % 创建测试管理主网格布局 (3行1列)
            app.TestingGrid = uigridlayout(app.TestingTab, [3, 1]);
            app.TestingGrid.RowHeight = {'fit', 'fit', '1x'};     % 顶部按钮行|中部面板区域|底部表格区域
            app.TestingGrid.ColumnWidth = {'1x'};
            app.TestingGrid.Padding = [20, 20, 20, 20];
            app.TestingGrid.RowSpacing = 15;

            % [1,1] 顶部Test Authoring Tool按钮（独占一行）
            app.TestAuthoringToolButton = uibutton(app.TestingGrid, 'push');
            app.TestAuthoringToolButton.Layout.Row = 1;
            app.TestAuthoringToolButton.Layout.Column = 1;
            app.TestAuthoringToolButton.Text = 'Test Authoring Tool';
            app.TestAuthoringToolButton.BackgroundColor = [0.4 0.6 0.2];
            app.TestAuthoringToolButton.FontColor = [1 1 1];
            app.TestAuthoringToolButton.FontWeight = 'bold';
            app.TestAuthoringToolButton.FontSize = 14;
            app.TestAuthoringToolButton.Tooltip = '打开测试用例编辑工具';

            % [2,1] 中部面板容器
            topPanelContainer = uipanel(app.TestingGrid);
            topPanelContainer.Layout.Row = 2;
            topPanelContainer.Layout.Column = 1;
            topPanelContainer.Title = '';
            topPanelContainer.BorderType = 'none';

            % 在中部容器内创建4列布局（原5列改为4列）
            topGrid = uigridlayout(topPanelContainer, [1, 4]);
            topGrid.RowHeight = {'1x'};
            topGrid.ColumnWidth = {'1x', '1x', '1x', '1x'};  % 4列等宽
            topGrid.Padding = [10, 10, 10, 10];
            topGrid.RowSpacing = 10;
            topGrid.ColumnSpacing = 15;

            % [1,1] Test Execution Panel
            app.TestExecutionPanel = uipanel(topGrid);
            app.TestExecutionPanel.Layout.Row = 1;
            app.TestExecutionPanel.Layout.Column = 1;
            app.TestExecutionPanel.Title = 'Test Execution';

            % 在Test Execution Panel内创建垂直布局 - 包含测试模式选择和打开Test Manager按钮
            eocGrid = uigridlayout(app.TestExecutionPanel, [3, 1]);
            eocGrid.RowHeight = {'fit', 'fit', '1x'};  % 下拉框|按钮|弹性空间
            eocGrid.ColumnWidth = {'1x'};
            eocGrid.Padding = [10, 10, 10, 10];
            eocGrid.RowSpacing = 5;

            app.TestModeDropDown = uidropdown(eocGrid);
            app.TestModeDropDown.Layout.Row = 1;
            app.TestModeDropDown.Layout.Column = 1;
            app.TestModeDropDown.Items = {'MIL', 'SIL', 'PIL'};
            app.TestModeDropDown.Value = 'MIL';
            app.TestModeDropDown.ValueChangedFcn = @app.TestModeDropDownValueChanged;
            
            % 添加"打开Test Manager"按钮
            app.OpenTestManagerButton = uibutton(eocGrid, 'push');
            app.OpenTestManagerButton.Layout.Row = 2;
            app.OpenTestManagerButton.Layout.Column = 1;
            app.OpenTestManagerButton.Text = 'Open Test Manager';
            app.OpenTestManagerButton.ButtonPushedFcn = @app.OpenTestManagerButtonPushed;
            app.OpenTestManagerButton.BackgroundColor = [0.4, 0.6, 0.8];
            app.OpenTestManagerButton.FontColor = [1 1 1];
            app.OpenTestManagerButton.FontWeight = 'bold';
            app.OpenTestManagerButton.Tooltip = '打开当前模型的Test Manager测试文件';

            % [1,2] HLR Test Panel
            app.HLRTestPanel = uipanel(topGrid);
            app.HLRTestPanel.Layout.Row = 1;
            app.HLRTestPanel.Layout.Column = 2;
            app.HLRTestPanel.Title = 'High Level Requirements Test';

            % 在HLR Panel内创建垂直布局（4行2列）
            hlrGrid = uigridlayout(app.HLRTestPanel, [4, 2]);
            hlrGrid.RowHeight = {'fit', 'fit', 'fit', 'fit'};  % 运行按钮|测试报告按钮+灯|覆盖度报告按钮+灯|说明文字
            hlrGrid.ColumnWidth = {'1x', 'fit'};
            hlrGrid.Padding = [10, 10, 10, 10];
            hlrGrid.RowSpacing = 5;
            hlrGrid.ColumnSpacing = 10;

            % 第1行：Run HLR Test 按钮
            app.RunHLRTestButton = uibutton(hlrGrid, 'push');
            app.RunHLRTestButton.Layout.Row = 1;
            app.RunHLRTestButton.Layout.Column = [1, 2];  % 跨2列
            app.RunHLRTestButton.Text = 'Run HLR Test';
            app.RunHLRTestButton.BackgroundColor = [0.8 0.4 0.2];
            app.RunHLRTestButton.FontColor = [1 1 1];
            app.RunHLRTestButton.FontWeight = 'bold';
            app.RunHLRTestButton.ButtonPushedFcn = @app.RunHLRTestButtonPushed;

            % 第2行：Open HLR Test Report 按钮 + 状态灯
            app.OpenHLRReportButton = uibutton(hlrGrid, 'push');
            app.OpenHLRReportButton.Layout.Row = 2;
            app.OpenHLRReportButton.Layout.Column = 1;
            app.OpenHLRReportButton.Text = 'Open Test Report';
            app.OpenHLRReportButton.BackgroundColor = [0.2 0.6 0.8];
            app.OpenHLRReportButton.FontColor = [1 1 1];
            app.OpenHLRReportButton.FontWeight = 'bold';
            app.OpenHLRReportButton.ButtonPushedFcn = @app.OpenHLRReportButtonPushed;
            app.OpenHLRReportButton.Tooltip = '打开HLR测试报告（PDF）';

            app.HLRTestLamp = uilamp(hlrGrid);
            app.HLRTestLamp.Layout.Row = 2;
            app.HLRTestLamp.Layout.Column = 2;

            % 第3行：Open HLR Coverage Report 按钮 + 状态灯
            app.OpenHLRCoverageButton = uibutton(hlrGrid, 'push');
            app.OpenHLRCoverageButton.Layout.Row = 3;
            app.OpenHLRCoverageButton.Layout.Column = 1;
            app.OpenHLRCoverageButton.Text = 'Open Coverage Report';
            app.OpenHLRCoverageButton.BackgroundColor = [0.3 0.7 0.4];
            app.OpenHLRCoverageButton.FontColor = [1 1 1];
            app.OpenHLRCoverageButton.FontWeight = 'bold';
            app.OpenHLRCoverageButton.ButtonPushedFcn = @app.OpenHLRCoverageButtonPushed;
            app.OpenHLRCoverageButton.Tooltip = '打开HLR覆盖度报告（HTML）';

            app.HLRCoverageLamp = uilamp(hlrGrid);
            app.HLRCoverageLamp.Layout.Row = 3;
            app.HLRCoverageLamp.Layout.Column = 2;

            % 第4行：说明文字（跨2列）
            hlrLabel = uilabel(hlrGrid);
            hlrLabel.Layout.Row = 4;
            hlrLabel.Layout.Column = [1, 2];
            hlrLabel.Text = 'High-level requirement-based testing';
            hlrLabel.FontSize = 10;
            hlrLabel.FontColor = [0.5 0.5 0.5];
            hlrLabel.HorizontalAlignment = 'center';

            % [1,3] LLR Test Panel
            app.LLRTestPanel = uipanel(topGrid);
            app.LLRTestPanel.Layout.Row = 1;
            app.LLRTestPanel.Layout.Column = 3;
            app.LLRTestPanel.Title = 'Low Level Requirements Test';

            % 在LLR Panel内创建垂直布局（4行2列）
            llrGrid = uigridlayout(app.LLRTestPanel, [4, 2]);
            llrGrid.RowHeight = {'fit', 'fit', 'fit', 'fit'};  % 运行按钮|测试报告按钮+灯|覆盖度报告按钮+灯|说明文字
            llrGrid.ColumnWidth = {'1x', 'fit'};
            llrGrid.Padding = [10, 10, 10, 10];
            llrGrid.RowSpacing = 5;
            llrGrid.ColumnSpacing = 10;

            % 第1行：Run LLR Test 按钮
            app.RunLLRTestButton = uibutton(llrGrid, 'push');
            app.RunLLRTestButton.Layout.Row = 1;
            app.RunLLRTestButton.Layout.Column = [1, 2];  % 跨2列
            app.RunLLRTestButton.Text = 'Run LLR Test';
            app.RunLLRTestButton.BackgroundColor = [0.6 0.2 0.8];
            app.RunLLRTestButton.FontColor = [1 1 1];
            app.RunLLRTestButton.FontWeight = 'bold';
            app.RunLLRTestButton.ButtonPushedFcn = @app.RunLLRTestButtonPushed;

            % 第2行：Open LLR Test Report 按钮 + 状态灯
            app.OpenLLRReportButton = uibutton(llrGrid, 'push');
            app.OpenLLRReportButton.Layout.Row = 2;
            app.OpenLLRReportButton.Layout.Column = 1;
            app.OpenLLRReportButton.Text = 'Open Test Report';
            app.OpenLLRReportButton.BackgroundColor = [0.2 0.6 0.8];
            app.OpenLLRReportButton.FontColor = [1 1 1];
            app.OpenLLRReportButton.FontWeight = 'bold';
            app.OpenLLRReportButton.ButtonPushedFcn = @app.OpenLLRReportButtonPushed;
            app.OpenLLRReportButton.Tooltip = '打开LLR测试报告（PDF）';

            app.LLRTestLamp = uilamp(llrGrid);
            app.LLRTestLamp.Layout.Row = 2;
            app.LLRTestLamp.Layout.Column = 2;

            % 第3行：Open LLR Coverage Report 按钮 + 状态灯
            app.OpenLLRCoverageButton = uibutton(llrGrid, 'push');
            app.OpenLLRCoverageButton.Layout.Row = 3;
            app.OpenLLRCoverageButton.Layout.Column = 1;
            app.OpenLLRCoverageButton.Text = 'Open Coverage Report';
            app.OpenLLRCoverageButton.BackgroundColor = [0.3 0.7 0.4];
            app.OpenLLRCoverageButton.FontColor = [1 1 1];
            app.OpenLLRCoverageButton.FontWeight = 'bold';
            app.OpenLLRCoverageButton.ButtonPushedFcn = @app.OpenLLRCoverageButtonPushed;
            app.OpenLLRCoverageButton.Tooltip = '打开LLR覆盖度报告（HTML）';

            app.LLRCoverageLamp = uilamp(llrGrid);
            app.LLRCoverageLamp.Layout.Row = 3;
            app.LLRCoverageLamp.Layout.Column = 2;

            % 第4行：说明文字（跨2列）
            llrLabel = uilabel(llrGrid);
            llrLabel.Layout.Row = 4;
            llrLabel.Layout.Column = [1, 2];
            llrLabel.Text = 'Low-level requirement-based testing';
            llrLabel.FontSize = 10;
            llrLabel.FontColor = [0.5 0.5 0.5];
            llrLabel.HorizontalAlignment = 'center';

            % [1,4] Coverage Panel
            app.CoveragePanel = uipanel(topGrid);
            app.CoveragePanel.Layout.Row = 1;
            app.CoveragePanel.Layout.Column = 4;
            app.CoveragePanel.Title = 'Coverage Analysis';

            % 在Coverage Panel内创建垂直布局
            coverageTestGrid = uigridlayout(app.CoveragePanel, [3, 1]);
            coverageTestGrid.RowHeight = {'fit', 'fit', 'fit'};  % 弹性空间|按钮|显示
            coverageTestGrid.ColumnWidth = {'1x'};
            coverageTestGrid.Padding = [10, 10, 10, 10];
            coverageTestGrid.RowSpacing = 5;

            app.MergeCoverageButton = uibutton(coverageTestGrid, 'push');
            app.MergeCoverageButton.Layout.Row = 2;
            app.MergeCoverageButton.Layout.Column = 1;
            app.MergeCoverageButton.Text = 'Merge Coverage';
            app.MergeCoverageButton.BackgroundColor = [0.3 0.7 0.4];
            app.MergeCoverageButton.FontColor = [1 1 1];
            app.MergeCoverageButton.FontWeight = 'bold';

            app.CoverageGauge = uilabel(coverageTestGrid);
            app.CoverageGauge.Layout.Row = 3;
            app.CoverageGauge.Layout.Column = 1;
            app.CoverageGauge.Text = 'Coverage: 0%';
            app.CoverageGauge.FontWeight = 'bold';
            app.CoverageGauge.HorizontalAlignment = 'center';
            app.CoverageGauge.FontSize = 12;
            app.CoverageGauge.BackgroundColor = [0.95 0.95 0.95];

            % [3,1] Test Management Panel - 底部大表格
            app.TestManagementPanel = uipanel(app.TestingGrid);
            app.TestManagementPanel.Layout.Row = 3;
            app.TestManagementPanel.Layout.Column = 1;
            app.TestManagementPanel.Title = 'HLR Test Case Management';

            % 在Test Management Panel内创建表格布局（2行1列：刷新按钮 + 表格）
            tableGrid = uigridlayout(app.TestManagementPanel, [2, 1]);
            tableGrid.RowHeight = {'fit', '1x'};      % 刷新按钮固定高度，表格占满剩余空间
            tableGrid.ColumnWidth = {'1x'};
            tableGrid.Padding = [10, 10, 10, 10];

            % 刷新按钮
            app.RefreshTestCasesButton = uibutton(tableGrid);
            app.RefreshTestCasesButton.Layout.Row = 1;
            app.RefreshTestCasesButton.Layout.Column = 1;
            app.RefreshTestCasesButton.Text = '🔄 Refresh Test Cases';
            app.RefreshTestCasesButton.BackgroundColor = [0.2, 0.8, 0.4];
            app.RefreshTestCasesButton.FontColor = [1 1 1];
            app.RefreshTestCasesButton.FontWeight = 'bold';
            app.RefreshTestCasesButton.Tooltip = '刷新当前模型的测试用例列表';

            % 测试用例表格
            app.TestCaseTable = uitable(tableGrid);
            app.TestCaseTable.Layout.Row = 2;
            app.TestCaseTable.Layout.Column = 1;
            app.TestCaseTable.ColumnName = {'Test ID', 'Description', 'Type', 'Status', 'Requirement', 'Start Time'};

            % 使用auto自动列宽
            app.TestCaseTable.ColumnWidth = {'auto', 'auto', 'auto', 'auto', 'auto', 'auto'};
            
            % 添加单元格选择回调（点击需求列打开链接）
            % 已禁用：跳转功能暂时关闭
            % app.TestCaseTable.CellSelectionCallback = @app.TestCaseTableCellSelected;
        end
        function createReportsTab(app)
            % 创建报告管理标签页 - 基于uigridlayout的Form+Grid混合布局
            app.ReportsTab = uitab(app.TabGroup);
            app.ReportsTab.Title = 'Reports & Deliverables (暂无有效功能)';

            % 创建报告管理网格布局 (2行2列)
            app.ReportsGrid = uigridlayout(app.ReportsTab, [2, 2]);
            app.ReportsGrid.RowHeight = {'fit', '1x'};        % 顶部面板区域|底部表格区域
            app.ReportsGrid.ColumnWidth = {'1x', '1x'};       % 2列等宽
            app.ReportsGrid.Padding = [20, 20, 20, 20];
            app.ReportsGrid.RowSpacing = 15;
            app.ReportsGrid.ColumnSpacing = 15;

            % [1,1] Report Generation Panel
            app.ReportGenerationPanel = uipanel(app.ReportsGrid);
            app.ReportGenerationPanel.Layout.Row = 1;
            app.ReportGenerationPanel.Layout.Column = 1;
            app.ReportGenerationPanel.Title = 'Report Generation';

            % 在Report Generation Panel内创建网格布局 (2行3列)
            genGrid = uigridlayout(app.ReportGenerationPanel, [2, 3]);
            genGrid.RowHeight = {'fit', 'fit'};  % 弹性空间|按钮行
            genGrid.ColumnWidth = {'1x', '1x', '1x'};  % 3列等宽按钮
            genGrid.Padding = [10, 10, 10, 10];
            genGrid.RowSpacing = 5;
            genGrid.ColumnSpacing = 10;

            % 弹性空间 [1,1-3] 自动填充

            % 按钮行 [2,1-3]
            app.GenerateTestReportButton = uibutton(genGrid, 'push');
            app.GenerateTestReportButton.Layout.Row = 2;
            app.GenerateTestReportButton.Layout.Column = 1;
            app.GenerateTestReportButton.Text = '项目测试报告';
            app.GenerateTestReportButton.BackgroundColor = [0.2 0.6 0.8];
            app.GenerateTestReportButton.FontColor = [1 1 1];
            app.GenerateTestReportButton.FontWeight = 'bold';

            app.GenerateCoverageReportButton = uibutton(genGrid, 'push');
            app.GenerateCoverageReportButton.Layout.Row = 2;
            app.GenerateCoverageReportButton.Layout.Column = 2;
            app.GenerateCoverageReportButton.Text = '项目覆盖度报告';
            app.GenerateCoverageReportButton.BackgroundColor = [0.8 0.4 0.2];
            app.GenerateCoverageReportButton.FontColor = [1 1 1];
            app.GenerateCoverageReportButton.FontWeight = 'bold';

            app.GenerateSDDButton = uibutton(genGrid, 'push');
            app.GenerateSDDButton.Layout.Row = 2;
            app.GenerateSDDButton.Layout.Column = 3;
            app.GenerateSDDButton.Text = 'SDD Report';
            app.GenerateSDDButton.BackgroundColor = [0.6 0.2 0.8];
            app.GenerateSDDButton.FontColor = [1 1 1];
            app.GenerateSDDButton.FontWeight = 'bold';

            % [1,2] DO-178C Deliverables Panel
            app.DeliverablePanel = uipanel(app.ReportsGrid);
            app.DeliverablePanel.Layout.Row = 1;
            app.DeliverablePanel.Layout.Column = 2;
            app.DeliverablePanel.Title = 'DO-178C Deliverables';

            % 在Deliverable Panel内创建垂直布局
            deliverableGrid = uigridlayout(app.DeliverablePanel, [3, 1]);
            deliverableGrid.RowHeight = {'fit', 'fit', 'fit'};  % 弹性空间|按钮1|按钮2
            deliverableGrid.ColumnWidth = {'1x'};
            deliverableGrid.Padding = [10, 10, 10, 10];
            deliverableGrid.RowSpacing = 10;

            app.ExportDeliverableButton = uibutton(deliverableGrid, 'push');
            app.ExportDeliverableButton.Layout.Row = 2;
            app.ExportDeliverableButton.Layout.Column = 1;
            app.ExportDeliverableButton.Text = 'Export Deliverable Package';
            app.ExportDeliverableButton.BackgroundColor = [0.3 0.7 0.3];
            app.ExportDeliverableButton.FontColor = [1 1 1];
            app.ExportDeliverableButton.FontWeight = 'bold';

            app.DO178ObjectivesButton = uibutton(deliverableGrid, 'push');
            app.DO178ObjectivesButton.Layout.Row = 3;
            app.DO178ObjectivesButton.Layout.Column = 1;
            app.DO178ObjectivesButton.Text = 'DO-178C Objectives';
            app.DO178ObjectivesButton.BackgroundColor = [0.7 0.5 0.2];
            app.DO178ObjectivesButton.FontColor = [1 1 1];
            app.DO178ObjectivesButton.FontWeight = 'bold';

            % [2,1] Report Status Panel - 左侧大表格
            app.ReportStatusPanel = uipanel(app.ReportsGrid);
            app.ReportStatusPanel.Layout.Row = 2;
            app.ReportStatusPanel.Layout.Column = 1;
            app.ReportStatusPanel.Title = 'Report Status';

            % 在Report Status Panel内创建表格布局
            statusTableGrid = uigridlayout(app.ReportStatusPanel, [1, 1]);
            statusTableGrid.RowHeight = {'1x'};
            statusTableGrid.ColumnWidth = {'1x'};
            statusTableGrid.Padding = [10, 10, 10, 10];

            app.ReportStatusTable = uitable(statusTableGrid);
            app.ReportStatusTable.Layout.Row = 1;
            app.ReportStatusTable.Layout.Column = 1;
            app.ReportStatusTable.ColumnName = {'Report Type', 'Status', 'Last Generated', 'Version', 'Actions'};

            % 使用auto自动列宽
            app.ReportStatusTable.ColumnWidth = {'auto', 'auto', 'auto', 'auto', 'auto'};

            % [2,2] Documentation Panel - 右侧小面板
            app.DocumentationPanel = uipanel(app.ReportsGrid);
            app.DocumentationPanel.Layout.Row = 2;
            app.DocumentationPanel.Layout.Column = 2;
            app.DocumentationPanel.Title = 'Documentation & Help';

            % 在Documentation Panel内创建垂直布局
            docGrid = uigridlayout(app.DocumentationPanel, [3, 1]);
            docGrid.RowHeight = {'fit', 'fit', 'fit'};  % 弹性空间|按钮|说明
            docGrid.ColumnWidth = {'1x'};
            docGrid.Padding = [10, 10, 10, 10];
            docGrid.RowSpacing = 10;

            app.ViewDocumentationButton = uibutton(docGrid, 'push');
            app.ViewDocumentationButton.Layout.Row = 2;
            app.ViewDocumentationButton.Layout.Column = 1;
            app.ViewDocumentationButton.Text = 'View Documentation';
            app.ViewDocumentationButton.BackgroundColor = [0.4 0.4 0.7];
            app.ViewDocumentationButton.FontColor = [1 1 1];
            app.ViewDocumentationButton.FontWeight = 'bold';

        end
        
        function createProjectConfigTab(app)
            % 创建项目配置标签页 - 基于uigridlayout的左右分栏布局
            app.ProjectConfigTab = uitab(app.TabGroup);
            app.ProjectConfigTab.Title = 'Project Configuration';

            % 创建项目配置网格布局 (2行2列)
            app.ProjectConfigGrid = uigridlayout(app.ProjectConfigTab, [2, 2]);
            app.ProjectConfigGrid.RowHeight = {'fit', '1x'};        % 按钮行|主内容区
            app.ProjectConfigGrid.ColumnWidth = {'0.3x', '0.7x'};   % 左侧30%|右侧70%
            app.ProjectConfigGrid.Padding = [20, 20, 20, 20];
            app.ProjectConfigGrid.RowSpacing = 15;
            app.ProjectConfigGrid.ColumnSpacing = 15;

            % [1,1] 刷新按钮 - 左上角
            app.RefreshConfigButton = uibutton(app.ProjectConfigGrid, 'push');
            app.RefreshConfigButton.Layout.Row = 1;
            app.RefreshConfigButton.Layout.Column = 1;
            app.RefreshConfigButton.Text = '🔄 刷新配置文件';
            app.RefreshConfigButton.BackgroundColor = [0.3 0.7 0.4];
            app.RefreshConfigButton.FontColor = [1 1 1];
            app.RefreshConfigButton.FontWeight = 'bold';


            % [2,1] 左侧配置文件树面板
            app.ConfigFileTreePanel = uipanel(app.ProjectConfigGrid);
            app.ConfigFileTreePanel.Layout.Row = 2;
            app.ConfigFileTreePanel.Layout.Column = 1;
            app.ConfigFileTreePanel.Title = '配置文件分类';

            % 在配置文件树面板内创建树控件布局
            treeGrid = uigridlayout(app.ConfigFileTreePanel, [1, 1]);
            treeGrid.RowHeight = {'1x'};
            treeGrid.ColumnWidth = {'1x'};
            treeGrid.Padding = [10, 10, 10, 10];

            % 创建配置文件树
            app.ConfigTree = uitree(treeGrid);
            app.ConfigTree.Layout.Row = 1;
            app.ConfigTree.Layout.Column = 1;

            % [2,2] 右侧信息显示面板 - 重构为3个独立面板的纵向布局
            infoMainPanel = uipanel(app.ProjectConfigGrid);
            infoMainPanel.Layout.Row = 2;
            infoMainPanel.Layout.Column = 2;
            infoMainPanel.Title = '信息面板';
            infoMainPanel.FontWeight = 'bold';

            % 创建主布局网格 (3行1列：文件夹信息|用户信息|版本信息)
            mainInfoGrid = uigridlayout(infoMainPanel, [3, 1]);
            mainInfoGrid.RowHeight = {'1x', 'fit', 'fit'};  % 文件信息自适应，其他固定
            mainInfoGrid.ColumnWidth = {'1x'};
            mainInfoGrid.Padding = [10, 10, 10, 10];
            mainInfoGrid.RowSpacing = 10;

            % [Row 1] 📁 文件夹信息面板
            app.FileInfoPanel = uipanel(mainInfoGrid);
            app.FileInfoPanel.Layout.Row = 1;
            app.FileInfoPanel.Layout.Column = 1;
            app.FileInfoPanel.Title = '📁 文件夹信息';
            app.FileInfoPanel.FontWeight = 'bold';
            
            % 文件信息面板内部布局 (3行1列：路径|信息|按钮)
            fileInfoGrid = uigridlayout(app.FileInfoPanel, [3, 1]);
            fileInfoGrid.RowHeight = {'fit', '1x', 'fit'};
            fileInfoGrid.ColumnWidth = {'1x'};
            fileInfoGrid.Padding = [10, 10, 10, 10];
            fileInfoGrid.RowSpacing = 8;

            % 文件路径显示
            app.ConfigFilePathLabel = uilabel(fileInfoGrid);
            app.ConfigFilePathLabel.Layout.Row = 1;
            app.ConfigFilePathLabel.Layout.Column = 1;
            app.ConfigFilePathLabel.Text = '请选择配置文件查看详细信息';
            app.ConfigFilePathLabel.FontSize = 12;
            app.ConfigFilePathLabel.FontWeight = 'bold';
            app.ConfigFilePathLabel.WordWrap = 'on';

            % 文件信息显示
            app.ConfigFileInfoLabel = uilabel(fileInfoGrid);
            app.ConfigFileInfoLabel.Layout.Row = 2;
            app.ConfigFileInfoLabel.Layout.Column = 1;
            app.ConfigFileInfoLabel.Text = '';
            app.ConfigFileInfoLabel.FontSize = 11;
            app.ConfigFileInfoLabel.VerticalAlignment = 'top';
            app.ConfigFileInfoLabel.WordWrap = 'on';

            % 打开选中文件按钮
            app.OpenConfigFileButton = uibutton(fileInfoGrid, 'push');
            app.OpenConfigFileButton.Layout.Row = 3;
            app.OpenConfigFileButton.Layout.Column = 1;
            app.OpenConfigFileButton.Text = '📂 打开选中文件';
            app.OpenConfigFileButton.BackgroundColor = [0.2 0.6 0.8];
            app.OpenConfigFileButton.FontColor = [1 1 1];
            app.OpenConfigFileButton.FontWeight = 'bold';
            app.OpenConfigFileButton.FontSize = 11;

            % [Row 2] 👤 用户信息面板
            app.UserInfoPanel = uipanel(mainInfoGrid);
            app.UserInfoPanel.Layout.Row = 2;
            app.UserInfoPanel.Layout.Column = 1;
            app.UserInfoPanel.Title = '👤 用户信息';
            app.UserInfoPanel.FontWeight = 'bold';
            
            % 用户信息面板内部布局 (3行1列：标签|编辑框|按钮)
            userGrid = uigridlayout(app.UserInfoPanel, [3, 1]);
            userGrid.RowHeight = {'fit', 'fit', 'fit'};
            userGrid.ColumnWidth = {'1x'};
            userGrid.Padding = [10, 10, 10, 10];
            userGrid.RowSpacing = 8;

            % 用户名标签
            app.UserNameLabel = uilabel(userGrid);
            app.UserNameLabel.Layout.Row = 1;
            app.UserNameLabel.Layout.Column = 1;
            app.UserNameLabel.Text = '当前用户:';
            app.UserNameLabel.FontSize = 11;
            app.UserNameLabel.FontWeight = 'bold';

            % 用户名编辑框
            app.UserNameEditField = uieditfield(userGrid, 'text');
            app.UserNameEditField.Layout.Row = 2;
            app.UserNameEditField.Layout.Column = 1;
            app.UserNameEditField.Placeholder = '请输入用户名...';
            app.UserNameEditField.FontSize = 11;

            % 保存用户名按钮
            app.SaveUserNameButton = uibutton(userGrid, 'push');
            app.SaveUserNameButton.Layout.Row = 3;
            app.SaveUserNameButton.Layout.Column = 1;
            app.SaveUserNameButton.Text = '💾 保存用户名';
            app.SaveUserNameButton.BackgroundColor = [0.2 0.6 0.8];
            app.SaveUserNameButton.FontColor = [1 1 1];
            app.SaveUserNameButton.FontWeight = 'bold';
            app.SaveUserNameButton.FontSize = 11;

            % [Row 3] 📋 工具版本信息面板
            app.VersionInfoPanel = uipanel(mainInfoGrid);
            app.VersionInfoPanel.Layout.Row = 3;
            app.VersionInfoPanel.Layout.Column = 1;
            app.VersionInfoPanel.Title = '📋 工具版本信息';
            app.VersionInfoPanel.FontWeight = 'bold';
            
            % 版本信息面板内部布局 (3行2列：标签|编辑框)
            versionGrid = uigridlayout(app.VersionInfoPanel, [3, 2]);
            versionGrid.RowHeight = {'fit', 'fit', 'fit'};
            versionGrid.ColumnWidth = {'fit', '1x'};
            versionGrid.Padding = [10, 10, 10, 10];
            versionGrid.RowSpacing = 8;
            versionGrid.ColumnSpacing = 10;

            % 软件版本
            app.SoftwareVersionLabel = uilabel(versionGrid);
            app.SoftwareVersionLabel.Layout.Row = 1;
            app.SoftwareVersionLabel.Layout.Column = 1;
            app.SoftwareVersionLabel.Text = '基线版本:';
            app.SoftwareVersionLabel.FontSize = 11;
            app.SoftwareVersionLabel.FontWeight = 'bold';

            app.SoftwareVersionEditField = uieditfield(versionGrid, 'text');
            app.SoftwareVersionEditField.Layout.Row = 1;
            app.SoftwareVersionEditField.Layout.Column = 2;
            app.SoftwareVersionEditField.Value = '';
            app.SoftwareVersionEditField.Editable = 'off';
            app.SoftwareVersionEditField.FontSize = 11;

            % 项目配置版本
            app.ProjectConfigVersionLabel = uilabel(versionGrid);
            app.ProjectConfigVersionLabel.Layout.Row = 2;
            app.ProjectConfigVersionLabel.Layout.Column = 1;
            app.ProjectConfigVersionLabel.Text = '项目配置版本:';
            app.ProjectConfigVersionLabel.FontSize = 11;
            app.ProjectConfigVersionLabel.FontWeight = 'bold';

            app.ProjectConfigVersionEditField = uieditfield(versionGrid, 'text');
            app.ProjectConfigVersionEditField.Layout.Row = 2;
            app.ProjectConfigVersionEditField.Layout.Column = 2;
            app.ProjectConfigVersionEditField.Value = '';
            app.ProjectConfigVersionEditField.Editable = 'off';
            app.ProjectConfigVersionEditField.FontSize = 11;

            % 工具版本
            app.ToolVersionLabel = uilabel(versionGrid);
            app.ToolVersionLabel.Layout.Row = 3;
            app.ToolVersionLabel.Layout.Column = 1;
            app.ToolVersionLabel.Text = '工具版本:';
            app.ToolVersionLabel.FontSize = 11;
            app.ToolVersionLabel.FontWeight = 'bold';

            app.ToolVersionEditField = uieditfield(versionGrid, 'text');
            app.ToolVersionEditField.Layout.Row = 3;
            app.ToolVersionEditField.Layout.Column = 2;
            app.ToolVersionEditField.Value = '';
            app.ToolVersionEditField.Editable = 'off';
            app.ToolVersionEditField.FontSize = 11;

            % 初始化配置文件树
            app.initializeConfigTree();
            
            % 初始化用户名（从getUserAccount获取）
            app.initializeUserName();
            
            % 初始化版本信息（暂时为空值）
            app.initializeVersionInfo();
        end
        
        function createALMTab(app)
            % 创建ALM标签页 - 基于uigridlayout的三列布局（左侧CR表格，中间PR表格，右侧按钮面板）
            app.ALMTab = uitab(app.TabGroup);
            app.ALMTab.Title = 'ALM';

            % 创建ALM网格布局 (1行3列：CR表格 | PR表格 | 按钮面板)
            app.ALMGrid = uigridlayout(app.ALMTab, [1, 3]);
            app.ALMGrid.RowHeight = {'1x'};
            app.ALMGrid.ColumnWidth = {'2x', '2x', '1x'};   % 2:2:1 比例
            app.ALMGrid.Padding = [20, 20, 20, 20];
            app.ALMGrid.ColumnSpacing = 15;

            % [1,1] 左侧CR表格面板
            app.CRTablePanel = uipanel(app.ALMGrid);
            app.CRTablePanel.Layout.Row = 1;
            app.CRTablePanel.Layout.Column = 1;
            app.CRTablePanel.Title = 'Change Request (CR)';

            % 在CR面板内创建布局：2行（过滤器 + 表格）
            crGrid = uigridlayout(app.CRTablePanel, [2, 1]);
            crGrid.RowHeight = {'fit', '1x'};  % 过滤器自适应 + 表格弹性
            crGrid.ColumnWidth = {'1x'};
            crGrid.Padding = [10, 10, 10, 10];
            crGrid.RowSpacing = 5;

            % 第1行：CR过滤器面板
            app.CRFilterPanel = app.createALMFilterPanel(crGrid, 1, 'CR');
            
            % 第2行：创建CR表格
            app.CRTable = uitable(crGrid);
            app.CRTable.Layout.Row = 2;
            app.CRTable.Layout.Column = 1;
            app.CRTable.ColumnName = {'数据库ID', 'CR编号', '标题', '状态', '优先级', '创建日期'};
            app.CRTable.ColumnWidth = {80, 150, 'auto', 'auto', 'auto', 'auto'};
            app.CRTable.Data = {};  % 初始为空，后续通过云端服务获取
            app.CRTable.SelectionType = 'row';  % 启用行选择

            % [1,2] 中间PR表格面板
            app.PRTablePanel = uipanel(app.ALMGrid);
            app.PRTablePanel.Layout.Row = 1;
            app.PRTablePanel.Layout.Column = 2;
            app.PRTablePanel.Title = 'Problem Report (PR)';

            % 在PR面板内创建布局：2行（过滤器 + 表格）
            prGrid = uigridlayout(app.PRTablePanel, [2, 1]);
            prGrid.RowHeight = {'fit', '1x'};  % 过滤器自适应 + 表格弹性
            prGrid.ColumnWidth = {'1x'};
            prGrid.Padding = [10, 10, 10, 10];
            prGrid.RowSpacing = 5;

            % 第1行：PR过滤器面板
            app.PRFilterPanel = app.createALMFilterPanel(prGrid, 1, 'PR');
            
            % 第2行：创建PR表格
            app.PRTable = uitable(prGrid);
            app.PRTable.Layout.Row = 2;
            app.PRTable.Layout.Column = 1;
            app.PRTable.ColumnName = {'数据库ID', 'PR编号', '标题', '状态', '严重性', '创建日期'};
            app.PRTable.ColumnWidth = {80, 150, 'auto', 'auto', 'auto', 'auto'};
            app.PRTable.Data = {};  % 初始为空，后续通过云端服务获取
            app.PRTable.SelectionType = 'row';  % 启用行选择

            % [1,3] 右侧按钮面板
            app.ALMButtonPanel = uipanel(app.ALMGrid);
            app.ALMButtonPanel.Layout.Row = 1;
            app.ALMButtonPanel.Layout.Column = 3;
            app.ALMButtonPanel.Title = '操作面板';

            % 在按钮面板内创建垂直布局（10行1列，包含分组空行）
            buttonGrid = uigridlayout(app.ALMButtonPanel, [10, 1]);
            buttonGrid.RowHeight = {'fit', 'fit', 20, 'fit', 'fit', 20, 'fit', 'fit', 20, 'fit'};  % 空行用固定20像素
            buttonGrid.ColumnWidth = {'1x'};
            buttonGrid.Padding = [15, 15, 15, 15];
            buttonGrid.RowSpacing = 8;

            % Row 1: 打开选中PR按钮
            app.OpenSelectedPRButton = uibutton(buttonGrid, 'push');
            app.OpenSelectedPRButton.Layout.Row = 1;
            app.OpenSelectedPRButton.Layout.Column = 1;
            app.OpenSelectedPRButton.Text = '📖 打开选中PR';
            app.OpenSelectedPRButton.BackgroundColor = [0.3 0.6 0.8];
            app.OpenSelectedPRButton.FontColor = [1 1 1];
            app.OpenSelectedPRButton.FontWeight = 'bold';
            app.OpenSelectedPRButton.ButtonPushedFcn = @(btn,event) app.onOpenSelectedPRButtonPushed();

            % Row 2: 打开选中CR按钮
            app.OpenSelectedCRButton = uibutton(buttonGrid, 'push');
            app.OpenSelectedCRButton.Layout.Row = 2;
            app.OpenSelectedCRButton.Layout.Column = 1;
            app.OpenSelectedCRButton.Text = '📖 打开选中CR';
            app.OpenSelectedCRButton.BackgroundColor = [0.6 0.4 0.7];
            app.OpenSelectedCRButton.FontColor = [1 1 1];
            app.OpenSelectedCRButton.FontWeight = 'bold';
            app.OpenSelectedCRButton.ButtonPushedFcn = @(btn,event) app.onOpenSelectedCRButtonPushed();

            % Row 4: 提交PR按钮
            app.SubmitPRButton = uibutton(buttonGrid, 'push');
            app.SubmitPRButton.Layout.Row = 4;
            app.SubmitPRButton.Layout.Column = 1;
            app.SubmitPRButton.Text = '📝 提交新PR';
            app.SubmitPRButton.BackgroundColor = [0.2 0.6 0.8];
            app.SubmitPRButton.FontColor = [1 1 1];
            app.SubmitPRButton.FontWeight = 'bold';
            app.SubmitPRButton.ButtonPushedFcn = @(btn,event) app.onSubmitPRButtonPushed();

            % Row 5: 审批PR按钮（仅admin可见）
            app.ApprovePRButton = uibutton(buttonGrid, 'push');
            app.ApprovePRButton.Layout.Row = 5;
            app.ApprovePRButton.Layout.Column = 1;
            app.ApprovePRButton.Text = '✅ 审批PR';
            app.ApprovePRButton.BackgroundColor = [0.1 0.7 0.1];
            app.ApprovePRButton.FontColor = [1 1 1];
            app.ApprovePRButton.FontWeight = 'bold';
            app.ApprovePRButton.Visible = 'off';  % 默认隐藏，admin权限检查后显示
            app.ApprovePRButton.ButtonPushedFcn = @(btn,event) app.onApprovePRButtonPushed();

            % Row 7: 提交CR按钮
            app.SubmitCRButton = uibutton(buttonGrid, 'push');
            app.SubmitCRButton.Layout.Row = 7;
            app.SubmitCRButton.Layout.Column = 1;
            app.SubmitCRButton.Text = '🔧 提交新CR';
            app.SubmitCRButton.BackgroundColor = [0.8 0.4 0.2];
            app.SubmitCRButton.FontColor = [1 1 1];
            app.SubmitCRButton.FontWeight = 'bold';
            app.SubmitCRButton.ButtonPushedFcn = @(btn,event) app.onSubmitCRButtonPushed();

            % Row 8: 审批CR按钮（仅admin可见）
            app.ApproveCRButton = uibutton(buttonGrid, 'push');
            app.ApproveCRButton.Layout.Row = 8;
            app.ApproveCRButton.Layout.Column = 1;
            app.ApproveCRButton.Text = '✅ 审批CR';
            app.ApproveCRButton.BackgroundColor = [0.7 0.5 0.1];
            app.ApproveCRButton.FontColor = [1 1 1];
            app.ApproveCRButton.FontWeight = 'bold';
            app.ApproveCRButton.Visible = 'off';  % 默认隐藏，admin权限检查后显示
            app.ApproveCRButton.ButtonPushedFcn = @(btn,event) app.onApproveCRButtonPushed();

            % Row 10: 刷新PR/CR按钮
            app.RefreshPRCRButton = uibutton(buttonGrid, 'push');
            app.RefreshPRCRButton.Layout.Row = 10;
            app.RefreshPRCRButton.Layout.Column = 1;
            app.RefreshPRCRButton.Text = '🔄 刷新PR/CR';
            app.RefreshPRCRButton.BackgroundColor = [0.3 0.7 0.4];
            app.RefreshPRCRButton.FontColor = [1 1 1];
            app.RefreshPRCRButton.FontWeight = 'bold';
            app.RefreshPRCRButton.ButtonPushedFcn = @(btn,event) app.onRefreshPRCRButtonPushed();
        end
        

        
        function setupCallbacks(app)
            % 设置所有回调函数 - Grid Layout系统的完整回调绑定
            app.MainFigure.CloseRequestFcn = @(src, event) app.MainFigureCloseRequest(src, event);
            
            % SizeChangedFcn 已移除 - 使用Grid Layout自动响应式布局
            
            % Menu callbacks
            % 主题菜单回调已在createDynamicThemeMenus中动态设置
            app.UserGuideMenu.MenuSelectedFcn = @(src, event) app.UserGuideMenuSelected(src, event);
            app.AboutMenu.MenuSelectedFcn = @(src, event) app.AboutMenuSelected(src, event);
            
            % Main control callbacks
            app.SwitchtoworkfolderButton.ButtonPushedFcn = @app.SwitchtoworkfolderButtonPushed;
            app.RefreshModelButton.ButtonPushedFcn = @app.RefreshModelButtonPushed;
            app.TopOpenModelButton.ButtonPushedFcn = @app.TopOpenModelButtonPushed;
            app.OpenICDButton.ButtonPushedFcn = @app.OpenICDButtonPushed;
            app.OpenSystemArchitectureButton.ButtonPushedFcn = @app.OpenSystemArchitectureButtonPushed;
            app.OpenProjectDocArchitectureButton.ButtonPushedFcn = @app.OpenProjectDocArchitectureButtonPushed;
            app.CurrentModelDropDown.ValueChangedFcn = @app.CurrentModelDropDownValueChanged;
            
            % SubTabGroup selection changed callback
            app.SubTabGroup.SelectionChangedFcn = @app.SubTabGroupSelectionChanged;
            
            % Model Management Tab - Extended callbacks (integrated Setup features)
            app.SDDReportButton.ButtonPushedFcn = @app.SDDReportButtonPushed;
            app.OpenSDDButton.ButtonPushedFcn = @app.OpenSDDButtonPushed;
            app.OpenSLDDButton.ButtonPushedFcn = @app.OpenSLDDButtonPushed;
            app.ViewModelDependencyButton.ButtonPushedFcn = @app.ViewModelDependencyButtonPushed;
            app.ViewFileDependencyButton.ButtonPushedFcn = @app.ViewFileDependencyButtonPushed;
            app.RunAllButton.ButtonPushedFcn = @app.RunAllButtonPushed;
            
            % Requirements Management Tab callbacks
            app.ImportCSVButton.ButtonPushedFcn = @app.ImportCSVButtonPushed;
            app.ImportReqIFButton.ButtonPushedFcn = @app.ImportReqIFButtonPushed;
            app.ExportToWordButton.ButtonPushedFcn = @app.ExportToWordButtonPushed;
            app.ViewTraceMatrixButton.ButtonPushedFcn = @app.ViewTraceMatrixButtonPushed;
            app.AddReqTabButton.ButtonPushedFcn = @app.AddReqTabButtonPushed;
            app.DeleteReqTabButton.ButtonPushedFcn = @app.DeleteReqTabButtonPushed;
            app.RefreshReqTreeButton.ButtonPushedFcn = @app.RefreshReqTreeButtonPushed;
            app.OpenRequirementFileButton.ButtonPushedFcn = @app.OpenRequirementFileButtonPushed;
            app.RequirementsTree.SelectionChangedFcn = @(src, event) app.RequirementsTreeSelectionChanged(src, event);
            
            % Model Management Tab callbacks
            app.NewModelButton.ButtonPushedFcn = @app.NewModelButtonPushed;
            app.ImportModelButton.ButtonPushedFcn = @app.ImportModelButtonPushed;
            app.RefreshTreeButton.ButtonPushedFcn = @app.RefreshTreeButtonPushed;
            app.SelectModelButton.ButtonPushedFcn = @app.SelectModelButtonPushed;
            app.OpenModelButton.ButtonPushedFcn = @app.OpenModelButtonPushed;
            app.DeleteModelButton.ButtonPushedFcn = @app.DeleteModelButtonPushed;
            app.ModelTree.SelectionChangedFcn = @app.ModelTreeSelectionChanged;
            
            % Model Dashboard callbacks
            app.RefreshModelDashboardButton.ButtonPushedFcn = @app.RefreshModelDashboardButtonPushed;
            app.OpenModelDashboardButton.ButtonPushedFcn = @app.OpenModelDashboardButtonPushed;
            
            % Model Analysis Tab callbacks
            app.OpenModelAdvisorButton.ButtonPushedFcn = @app.OpenModelAdvisorButtonPushed;
            app.RunModelAdvisorButton.ButtonPushedFcn = @app.RunModelAdvisorButtonPushed;
            app.OpenReportButton.ButtonPushedFcn = @app.OpenReportButtonPushed;
            app.ComputeModelMetricsButton.ButtonPushedFcn = @app.ComputeModelMetricsButtonPushed;
            app.ModelMetricsOpenReportButton.ButtonPushedFcn = @app.ModelMetricsOpenReportButtonPushed;
            app.OpenSLDVButton.ButtonPushedFcn = @app.OpenSLDVButtonPushed;
            app.RunSLDVButton.ButtonPushedFcn = @app.RunSLDVButtonPushed;
            app.SLDVOpenReportButton.ButtonPushedFcn = @app.SLDVOpenReportButtonPushed;
            app.CreateModelChecklistButton.ButtonPushedFcn = @app.CreateModelChecklistButtonPushed;
            app.OpenModelChecklistButton.ButtonPushedFcn = @app.OpenModelChecklistButtonPushed;
            
            % Code Generation Tab callbacks
            app.RunAllCodeAnalysisButton.ButtonPushedFcn = @app.RunAllCodeAnalysisButtonPushed;
            app.GenerateCodeButton.ButtonPushedFcn = @app.GenerateCodeButtonPushed;
            app.OpenCodeReportButton.ButtonPushedFcn = @app.OpenCodeReportButtonPushed;
            app.RunCodeInspectorButton.ButtonPushedFcn = @app.RunCodeInspectorButtonPushed;
            app.OpenCodeInspectorReportButton.ButtonPushedFcn = @app.OpenCodeInspectorReportButtonPushed;
            app.RunBugFinderButton.ButtonPushedFcn = @app.RunBugFinderButtonPushed;
            app.OpenBugFinderReportButton.ButtonPushedFcn = @app.OpenBugFinderReportButtonPushed;
            app.RunCodeStandardsButton.ButtonPushedFcn = @app.RunCodeStandardsButtonPushed;
            app.OpenCodeStandardsReportButton.ButtonPushedFcn = @app.OpenCodeStandardsReportButtonPushed;
            app.RunCodeProverButton.ButtonPushedFcn = @app.RunCodeProverButtonPushed;
            app.OpenCodeProverReportButton.ButtonPushedFcn = @app.OpenCodeProverReportButtonPushed;
            app.ComputeCodeMetricsButton.ButtonPushedFcn = @app.ComputeCodeMetricsButtonPushed;
            app.OpenCodeMetricsReportButton.ButtonPushedFcn = @app.OpenCodeMetricsReportButtonPushed;
            
            % Testing Management Tab callbacks
            app.TestModeDropDown.ValueChangedFcn = @app.TestModeDropDownValueChanged;
            app.RunHLRTestButton.ButtonPushedFcn = @app.RunHLRTestButtonPushed;
            app.RunLLRTestButton.ButtonPushedFcn = @app.RunLLRTestButtonPushed;
            app.MergeCoverageButton.ButtonPushedFcn = @app.MergeCoverageButtonPushed;
            app.TestAuthoringToolButton.ButtonPushedFcn = @app.TestAuthoringToolButtonPushed;
            app.RefreshTestCasesButton.ButtonPushedFcn = @app.RefreshTestCasesButtonPushed;
            
            % Reports & Deliverables Tab callbacks
            app.GenerateTestReportButton.ButtonPushedFcn = @app.GenerateTestReportButtonPushed;
            app.GenerateCoverageReportButton.ButtonPushedFcn = @app.GenerateCoverageReportButtonPushed;
            app.GenerateSDDButton.ButtonPushedFcn = @app.GenerateSDDButtonPushed;
            app.ExportDeliverableButton.ButtonPushedFcn = @app.ExportDeliverableButtonPushed;
            app.DO178ObjectivesButton.ButtonPushedFcn = @app.DO178ObjectivesButtonPushed;
            app.ViewDocumentationButton.ButtonPushedFcn = @app.ViewDocumentationButtonPushed;
            
            % Project Configuration Tab callbacks
            app.RefreshConfigButton.ButtonPushedFcn = @app.RefreshConfigButtonPushed;
            app.OpenConfigFileButton.ButtonPushedFcn = @app.OpenConfigFileButtonPushed;
            app.ConfigTree.SelectionChangedFcn = @(src, event) app.ConfigTreeSelectionChanged(src, event);
            app.SaveUserNameButton.ButtonPushedFcn = @app.SaveUserNameButtonPushed;
        end
        
        function initializeSampleData(app)
            % 初始化数据和工具箱路径
            
            % 基于规则第4先初始化业务逻辑服务层，再使用服务功能
            % 必须在调用任何依赖服务的方法之前完成服务初始化
            app.initializeServices();
            
            % 添加项目配置路径
            addpath(fullfile(fileparts(mfilename('fullpath')), '..', '..', 'project_configuration'));
            
            % 添加当前apps目录路径以确保refreshFolderTree等函数可用
            addpath(fullfile(fileparts(mfilename('fullpath'))));
            
            % 刷新模型列表和模型树
            app.refreshModelList();
            app.refreshModelTree();
            
            % 初始化模型操作按钮状态
            app.SelectModelButton.Enable = 'off';
            app.OpenModelButton.Enable = 'off';
            app.DeleteModelButton.Enable = 'off';
            
            % 创建默认的需求Tab（替代原来的HLR和SRS固定Tab）
            app.createDynamicReqTab('defaultReq');
            
            % 加载默认的slreqx文件到Requirements Status表格（依赖ReqService）
            app.loadDefaultSLREQXFile();
            
            % 初始化需求文件树
            app.initializeRequirementsTree();
            
            % 初始化Test Case Table为空
            app.TestCaseTable.Data = {};
            
            % 初始化Report Status Table为空
            app.ReportStatusTable.Data = {};
            
            % 打开工具时自动切换到work folder（基于规则第4条：架构分离原则）
            try
                toWorkFolder();
                if ~isempty(app.logger)
                    app.logger.logInfo(sprintf('已自动切换到工作文件夹: %s', pwd));
                end
            catch ME
                warning('mbdWorkflowTool:AutoSwitchWorkFolder', ...
                    '自动切换到工作文件夹失败: %s', ME.message);
                if ~isempty(app.logger)
                    app.logger.logWarning(sprintf('自动切换到工作文件夹失败: %s', ME.message));
                end
            end
        end
        
        function initializeServices(app)
            % 初始化所有业务逻辑服务 - 在app启动时一次性初始化
            % 更新于2025-10-14：整合Service Layer重构
            
            % 1. 初始化ALM集成服务
            app.initializeALMService();
            
            % 2. 初始化需求管理服务
            app.initializeRequirementsService();
            
            % 3. 初始化模型管理服务
            app.initializeModelManagementService();
            
            % 4. 初始化测试管理服务
            app.initializeTestManagementService();
            
            % 5. 初始化代码分析服务
            app.initializeCodeAnalysisService();
            
            % ===== 新Service Layer初始化 (2025-10-14 重构) =====
            % 6. 初始化模型静态分析服务
            app.initializeModelStaticAnalysisService();
            
            % 7. 初始化测试管理服务（使用TestManagementService）
            app.initializeTestManagementService();
            
            % 8. 初始化代码测试服务
            app.initializeCodeTestingService();
            
            % 9. 初始化项目管理服务
            app.initializeProjectManagementService();
        end
        
        function initializeALMService(app)
            % 初始化ALM集成服务 - 在app启动时一次性初始化
            % 基于规则第4使用ALMIntegrationService替代直接使用ALMCloudService
            % 基于用户需求：即使ALM服务未启动，也不阻塞APP启动
            % 避免每次调用时重复创建服务实例，提高性能
            
            try
                fprintf('🔄 正在初始化ALM集成服务...\n');
                
                % 获取ALM集成服务单例实例（会自动初始化ALMCloudService）
                % DebugMode默认为false，不输出调试信息
                app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                
                % 测试连接（可能会超时，但已包裹在try-catch中）
                [success, result] = app.ALMService.testConnection();
                if success
                    fprintf('✅ ALM集成服务初始化成功\n');
                    app.setMessage('ALM集成服务初始化成功', 'success');
                    
                    % 自动获取PR/CR数据一次（包裹在独立的try-catch中，避免阻塞）
                    try
                        app.autoRefreshPRCRData();
                    catch ME_refresh
                        fprintf('⚠️ ALM数据自动刷新失败: %s\n', ME_refresh.message);
                        % 不影响APP启动，静默失败
                    end
                else
                    fprintf('⚠️ ALM服务连接测试失败，将在后续调用时重试\n');
                    app.setMessage('ALM集成服务连接测试失败，将在需要时重试', 'warning');
                end
                
            catch ME
                fprintf('❌ ALM集成服务初始化失败: %s\n', ME.message);
                fprintf('   APP将继续启动，ALM功能将在后续调用时重试\n');
                app.setMessage('ALM集成服务暂不可用，APP已正常启动', 'warning');
                
                % 即使初始化失败，也创建一个空实例，避免后续调用出错
                try
                    app.ALMService = ALMIntegrationService.getInstance();
                catch
                    % 如果连实例都无法创建，则设为空，后续调用时再尝试
                    app.ALMService = [];
                    fprintf('   ALM服务实例创建失败，将在首次使用时重新初始化\n');
                end
            end
        end
        
        function initializeRequirementsService(app)
            % 初始化需求管理服务 - 在app启动时一次性初始化
            % 使用单例模式（规则要求）
            
            try
                fprintf('🔄 正在初始化需求管理服务...\n');
                
                % 获取需求管理服务单例实例
                app.ReqService = RequirementsManagementService.getInstance();
                
                fprintf('✅ 需求管理服务初始化成功\n');
                
            catch ME
                fprintf('❌ 需求管理服务初始化失败: %s\n', ME.message);
                fprintf('   APP将继续启动，需求管理功能将在后续调用时重试\n');
                
                % 即使初始化失败，也获取单例实例
                try
                    app.ReqService = RequirementsManagementService.getInstance();
                catch
                    % 如果连实例都无法创建，则设为空
                    app.ReqService = [];
                    fprintf('   需求管理服务实例创建失败，将在首次使用时重新初始化\n');
                end
            end
        end
        function initializeModelManagementService(app)
            % 初始化模型管理服务 - 在app启动时一次性初始化
            % 基于规则第4使用ModelManagementService管理模型业务逻辑
            % 使用单例模式（规则要求）
            
            try
                fprintf('🔄 正在初始化模型管理服务...\n');
                
                % 获取模型管理服务单例实例
                app.ModelService = ModelManagementService.getInstance();
                
                fprintf('✅ 模型管理服务初始化成功\n');
                
            catch ME
                fprintf('❌ 模型管理服务初始化失败: %s\n', ME.message);
                fprintf('   APP将继续启动，模型管理功能将在后续调用时重试\n');
                
                % 即使初始化失败，也获取单例实例
                try
                    app.ModelService = ModelManagementService.getInstance();
                catch
                    % 如果连实例都无法创建，则设为空
                    app.ModelService = [];
                    fprintf('   模型管理服务实例创建失败，将在首次使用时重新初始化\n');
                end
            end
        end
        
        function initializeTestManagementService(app)
            % 初始化测试管理服务 - 在app启动时一次性初始化
            % 基于规则第4条使用TestManagementService管理所有测试业务逻辑
            % 功能：HLR/LLR测试（MIL/SIL/PIL）、测试生成、覆盖率合并、测试环境清理
            % 2025-11-02更新：合并原ModelTestingService功能
            
            try
                fprintf('[INFO] 正在初始化测试管理服务...\n');
                
                % 获取测试管理服务单例实例（环境：DEV模式）
                app.TestService = TestManagementService.getInstance('Environment', 'DEV');
                
                fprintf('[OK] 测试管理服务初始化成功\n');
                
            catch ME
                fprintf('[ERROR] 测试管理服务初始化失败: %s\n', ME.message);
                fprintf('   APP将继续启动，测试功能将在后续调用时重试\n');
                
                % 即使初始化失败，也创建一个空实例
                try
                    app.TestService = TestManagementService.getInstance();
                catch
                    app.TestService = [];
                    fprintf('   测试管理服务实例创建失败，将在首次使用时重新初始化\n');
                end
            end
        end
        
        function initializeCodeAnalysisService(app)
            % 初始化代码分析服务 - 在app启动时一次性初始化
            % 基于规则第4使用CodeAnalysisService管理代码分析业务逻辑
            
            try
                fprintf('🔄 正在初始化代码分析服务...\n');
                
                % 获取代码分析服务单例实例
                app.CodeService = CodeAnalysisService.getInstance();
                
                fprintf('✅ 代码分析服务初始化成功\n');
                
            catch ME
                fprintf('❌ 代码分析服务初始化失败: %s\n', ME.message);
                fprintf('   APP将继续启动，代码分析功能将在后续调用时重试\n');
                
                % 即使初始化失败，也创建一个空实例，避免后续调用出错
                try
                    app.CodeService = CodeAnalysisService.getInstance();
                catch
                    % 如果连实例都无法创建，则设为空
                    app.CodeService = [];
                    fprintf('   代码分析服务实例创建失败，将在首次使用时重新初始化\n');
                end
            end
        end
        
        % ==================== 新Service Layer初始化方法 (2025-10-14 重构) ====================
        
        function initializeModelStaticAnalysisService(app)
            % 初始化模型静态分析服务 - 在app启动时一次性初始化
            % 基于规则第4使用ModelStaticAnalysisService管理模型静态分析业务逻辑
            % 功能：模型标准检查、模型指标计算、SDD生成、配置管理、Checklist生成
            
            try
                fprintf('🔄 正在初始化模型静态分析服务...\n');
                
                % 获取模型静态分析服务单例实例（环境：DEV模式）
                app.ModelStaticAnalysisService = ModelStaticAnalysisService.getInstance('Environment', 'DEV');
                
                fprintf('✅ 模型静态分析服务初始化成功\n');
                
            catch ME
                fprintf('❌ 模型静态分析服务初始化失败: %s\n', ME.message);
                fprintf('   APP将继续启动，模型静态分析功能将在后续调用时重试\n');
                
                % 即使初始化失败，也创建一个空实例
                try
                    app.ModelStaticAnalysisService = ModelStaticAnalysisService.getInstance();
                catch
                    app.ModelStaticAnalysisService = [];
                    fprintf('   模型静态分析服务实例创建失败，将在首次使用时重新初始化\n');
                end
            end
        end
        
        function initializeCodeTestingService(app)
            % 初始化代码测试服务 - 在app启动时一次性初始化
            % 基于规则第4使用CodeTestingService管理代码测试业务逻辑
            % 功能：代码级HLR/LLR测试、代码覆盖率合并
            
            try
                fprintf('🔄 正在初始化代码测试服务...\n');
                
                % 获取代码测试服务单例实例（环境：DEV模式）
                app.CodeTestingService = CodeTestingService.getInstance('Environment', 'DEV');
                
                fprintf('✅ 代码测试服务初始化成功\n');
                
            catch ME
                fprintf('❌ 代码测试服务初始化失败: %s\n', ME.message);
                fprintf('   APP将继续启动，代码测试功能将在后续调用时重试\n');
                
                % 即使初始化失败，也创建一个空实例
                try
                    app.CodeTestingService = CodeTestingService.getInstance();
                catch
                    app.CodeTestingService = [];
                    fprintf('   代码测试服务实例创建失败，将在首次使用时重新初始化\n');
                end
            end
        end
        
        function initializeProjectManagementService(app)
            % 初始化项目管理服务 - 在app启动时一次性初始化
            % 基于规则第4使用ProjectManagementService管理项目管理业务逻辑
            % 使用单例模式（规则要求）
            % 功能：模型创建、删除、项目结构管理、依赖检查
            
            try
                fprintf('🔄 正在初始化项目管理服务...\n');
                
                % 获取项目管理服务单例实例（环境：DEV模式）
                app.ProjectManagementService = ProjectManagementService.getInstance('Environment', 'DEV');
                
                fprintf('✅ 项目管理服务初始化成功\n');
                
            catch ME
                fprintf('❌ 项目管理服务初始化失败: %s\n', ME.message);
                fprintf('   APP将继续启动，项目管理功能将在后续调用时重试\n');
                
                % 即使初始化失败，也获取单例实例
                try
                    app.ProjectManagementService = ProjectManagementService.getInstance();
                catch
                    app.ProjectManagementService = [];
                    fprintf('   项目管理服务实例创建失败，将在首次使用时重新初始化\n');
                end
            end
        end
        

        function autoRefreshPRCRData(app)
            % 自动刷新PR/CR数据 - 在app启动时调用一次（无进度条）
            fprintf('🔄 正在自动获取PR/CR数据...\n');
            app.setMessage('正在获取最新的PR/CR数据...', 'info');
            
            % 调用核心刷新逻辑，不显示进度条
            app.refreshPRCRDataCore(false);
        end
        
        function refreshPRCRDataCore(app, showProgressBar)
            % PR/CR数据刷新核心逻辑 - 被自动刷新和手动刷新共同调用
            % 基于规则第4使用ALMIntegrationService和ALMDataTransformer
            % 基于用户需求：即使ALM服务未启动，也不阻塞APP启动
            % 输入：showProgressBar - 是否显示进度条（自动刷新为false，手动刷新为true）
            
            d = [];
            try
                % 根据需要显示进度条
                if showProgressBar
                    d = uiprogressdlg(app.MainFigure, 'Title', '刷新数据', ...
                        'Message', '正在从ALM云端服务获取PR/CR信息...', 'Indeterminate', 'on');
                end
                
                % 确保ALM服务已初始化
                if isempty(app.ALMService)
                    try
                        app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                    catch ME_init
                        fprintf('❌ 无法初始化ALM集成服务: %s\n', ME_init.message);
                        if ~isempty(d) && isvalid(d)
                            close(d);
                        end
                        app.setMessage('ALM服务暂不可用，请检查服务状态', 'error');
                        return;
                    end
                end
                
                % 获取PR数据（通过ALMIntegrationService）
                [prSuccess, prData] = app.ALMService.refreshPRList();
                if prSuccess && ~isempty(prData)
                    % 使用ALMDataTransformer转换为表格格式
                    prTableData = ALMDataTransformer.convertPRDataToTable(prData);
                    app.PRTable.Data = prTableData;
                    % 保存原始数据用于过滤
                    app.PROriginalData = prTableData;
                    % 基于规则第3条：动态更新过滤器选项
                    app.updateALMFilterOptions('PR', prTableData);
                    
                    % 重新应用当前的过滤器设置（如果不是"All"）
                    if ~strcmp(app.PRStatusFilter.Value, 'All') || ...
                       ~strcmp(app.PRSeverityFilter.Value, 'All') || ...
                       ~isempty(app.PRSearchField.Value)
                        fprintf('🔄 重新应用PR过滤器设置...\n');
                        app.applyALMFilter('PR');
                    end
                    
                    fprintf('✅ 成功获取 %d 条PR记录\n', size(prTableData, 1));
                else
                    % 无法获取数据时清空表格
                    app.PRTable.Data = {};
                    app.PROriginalData = {};
                    % 重置过滤器选项为只有"All"
                    app.updateALMFilterOptions('PR', {});
                end
                
                % 获取CR数据（通过ALMIntegrationService）
                [crSuccess, crData] = app.ALMService.refreshCRList();
                if crSuccess && ~isempty(crData)
                    % 使用ALMDataTransformer转换为表格格式
                    crTableData = ALMDataTransformer.convertCRDataToTable(crData);
                    app.CRTable.Data = crTableData;
                    % 保存原始数据用于过滤
                    app.CROriginalData = crTableData;
                    % 基于规则第3条：动态更新过滤器选项
                    app.updateALMFilterOptions('CR', crTableData);
                    
                    % 重新应用当前的过滤器设置（如果不是"All"）
                    if ~strcmp(app.CRStatusFilter.Value, 'All') || ...
                       ~strcmp(app.CRPriorityFilter.Value, 'All') || ...
                       ~isempty(app.CRSearchField.Value)
                        fprintf('🔄 重新应用CR过滤器设置...\n');
                        app.applyALMFilter('CR');
                    end
                    
                    fprintf('✅ 成功获取 %d 条CR记录\n', size(crTableData, 1));
                else
                    % 无法获取数据时清空表格
                    app.CRTable.Data = {};
                    app.CROriginalData = {};
                    % 重置过滤器选项为只有"All"
                    app.updateALMFilterOptions('CR', {});
                end
                
                % 关闭进度条
                if ~isempty(d)
                    close(d);
                    d = [];
                end
                
                % 显示结果（对于手动刷新显示详细信息）
                if showProgressBar
                    app.showRefreshResult(prSuccess, crSuccess, prData, crData);
                else
                    % 自动刷新只显示简单消息
                    if prSuccess || crSuccess
                        app.setMessage('PR/CR数据获取完成', 'success');
                    else
                        app.setMessage('无法获取最新数据，请检查网络连接和服务器状态', 'warning');
                    end
                end
                
            catch ME
                % 关闭进度条
                if ~isempty(d)
                    close(d);
                end
                
                fprintf('❌ 刷新PR/CR数据异常: %s\n', ME.message);
                
                if showProgressBar
                    % 手动刷新显示详细错误对话框
                    errorMsg = sprintf('刷新PR/CR数据失败：%s', ME.message);
                    uialert(app.MainFigure, errorMsg, '刷新失败', 'Icon', 'error');
                    app.setMessage(sprintf('❌ %s', errorMsg));
                    % figure(app.MainFigure) #tmpfix;
                else
                    % 自动刷新只显示消息
                    app.setMessage(sprintf('获取数据异常: %s', ME.message), 'error');
                end
            end
        end
        
        function showRefreshResult(app, prSuccess, crSuccess, prData, crData)
            % 显示手动刷新的详细结果
            
            if prSuccess || crSuccess
                successMsg = '✅ 数据刷新完成！';
                if prSuccess && crSuccess
                    successMsg = sprintf('%s\n已更新PR（%d条）和CR（%d条）数据。', ...
                        successMsg, length(prData), length(crData));
                elseif prSuccess
                    successMsg = sprintf('%s\n已更新PR（%d条）数据，CR数据获取失败。', ...
                        successMsg, length(prData));
                else
                    successMsg = sprintf('%s\n已更新CR（%d条）数据，PR数据获取失败。', ...
                        successMsg, length(crData));
                end
                uialert(app.MainFigure, successMsg, '刷新结果', 'Icon', 'success');
            else
                successMsg = '❌ 无法连接ALM服务器，数据获取失败';
                uialert(app.MainFigure, successMsg, '刷新结果', 'Icon', 'error');
            end
            
            % 更新消息区域
            app.setMessage(successMsg);
            % figure(app.MainFigure) #tmpfix;
        end
        
        function setMessage(app, message, type)
            % 设置消息显示 - 在MessagesTextArea中显示消息
            % 输入：message - 消息内容, type - 消息类型 ('info', 'success', 'warning', 'error')
            
            if nargin < 3
                type = 'info';
            end
            
            % 添加时间戳和类型标识
            timestamp = char(datetime('now', 'Format', 'HH:mm:ss'));
            
            switch type
                case 'success'
                    prefix = '✅';
                case 'warning'  
                    prefix = '⚠️';
                case 'error'
                    prefix = '❌';
                otherwise
                    prefix = 'ℹ️';
            end
            
            formattedMessage = sprintf('[%s] %s %s', timestamp, prefix, message);
            
            % 更新消息文本区域
            if isvalid(app.MessagesTextArea)
                app.MessagesTextArea.Value = formattedMessage;
            end
        end
        
        function applyTheme(app, themeName)
            % 应用主题
            % 所有主题业务逻辑由ThemeManager服务层处理
            % 不添加fallback机制
            % 不添加try-catch，让错误明确报告
            
            % 直接使用服务层的主题名称（无需映射）
            % ThemeManager会自动验证主题名称的有效性
            app.ThemeMgr.applyTheme(app.MainFigure, themeName);
            
            % 更新内部主题状态
            app.CurrentTheme = themeName;
            
            % 如果支持原生主题，且主题名称是原生支持的，则同步原生主题状态
            % MATLAB原生主题只支持'light'和'dark'
            if isprop(app.MainFigure, 'Theme') && ismember(themeName, {'light', 'dark'})
                app.MainFigure.Theme = themeName;
                % 注意：不设置ThemeMode属性，使用MATLAB默认行为
            end
        end
        
        function ThemeMenuSelected(app, themeName)
            % 主题菜单选中回调 - 统一的动态主题切换处理
            %
            % 参数：
            %   themeName - 主题内部名称（由Service Layer提供）
            
            app.applyTheme(themeName);
        end
        
        function applyCurrentThemeToDialog(app, dialogFigure)
            % 应用当前主题到对话框
            % 基于项目编码规则第4条：架构分离原则 - 调用Service Layer
            %
            % 参数：
            %   dialogFigure - 对话框figure句柄
            %
            % 示例：
            %   dlg = uifigure('Name', 'My Dialog');
            %   app.applyCurrentThemeToDialog(dlg);
            
            if nargin < 2 || ~isvalid(dialogFigure)
                return;
            end
            
            % 调用ThemeManager的静态方法应用主题
            ThemeManager.applyThemeToDialog(dialogFigure, app.CurrentTheme);
        end
        
        function dlg = createThemedProgressDialog(app, parentFigure, title, message)
            % 创建带主题的进度对话框
            % 基于项目编码规则第8条：GUI友好界面 - 所有长时间操作都应有进度条
            %
            % 参数：
            %   parentFigure - 父窗口句柄
            %   title - 对话框标题
            %   message - 显示消息
            %
            % 返回：
            %   dlg - 进度对话框句柄
            %
            % 示例：
            %   d = app.createThemedProgressDialog(app.MainFigure, '请稍候', '正在处理...');
            %   % 处理完成后
            %   close(d);
            
            dlg = uiprogressdlg(parentFigure, 'Title', title, ...
                'Message', message, 'Indeterminate', 'on');
            
            % 注意：uiprogressdlg 不支持直接应用主题，它会自动跟随父窗口主题
            % 如果将来需要自定义样式，可以在这里添加
        end
        
        function dlg = createThemedDialog(app, title, position, varargin)
            % 创建带主题的模态对话框
            % 基于项目编码规则第4条：架构分离原则
            %
            % 参数：
            %   title - 对话框标题
            %   position - 对话框位置 [x, y, width, height]
            %   varargin - 其他uifigure属性（Name-Value pairs）
            %
            % 返回：
            %   dlg - 对话框figure句柄
            %
            % 示例：
            %   dlg = app.createThemedDialog('设置', [100 100 400 300], 'Resize', 'off');
            %   % 添加控件...
            %   % 显示对话框
            
            dlg = uifigure('Name', title, ...
                'Position', position, ...
                'WindowStyle', 'modal', ...
                varargin{:});
            
            % 应用当前主题
            app.applyCurrentThemeToDialog(dlg);
        end
        
        function onThemeChanged(app, src, ~)
            % MATLAB 2025a原生主题变化回调函数
            % 当Figure的Theme属性改变时自动调用
            
                % 获取当前主题
                themeStyle = src.Theme.BaseColorStyle; % "light" 或 "dark"
                
                % 更新应用程序的主题状态
                app.CurrentTheme = themeStyle;
        end
        
        function MainFigureCloseRequest(app, varargin)
            % 应用关闭时的清理回调
            try
                if ~isempty(app) && isvalid(app) && isvalid(app.MainFigure)
                    delete(app.MainFigure);
                end
            catch
                % ignore and proceed to delete app handle
            end
            try
                if ~isempty(app) && isvalid(app)
                    delete(app);
                end
            catch
                % swallow any errors during cleanup
            end
        end
        
        % === Setup Tab Callbacks ===
        function SDDReportButtonPushed(app, varargin)
            % 生成SDD报告
            try
                if ~isempty(app.logger)
                    app.logger.logInfo('用户点击SDD报告生成按钮');
                end
                
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保ModelStaticAnalysisService已初始化
                if isempty(app.ModelStaticAnalysisService)
                    app.ModelStaticAnalysisService = ModelStaticAnalysisService('Environment', 'DEV');
                end
                
                app.startLogging();
                d = uiprogressdlg(app.MainFigure, ...
                    'Title', '📄 SDD Report Generation', ...
                    'Message', 'Generating system description document...', ...
                    'Indeterminate', 'on');
                
                % 基于规则第4调用ModelStaticAnalysisService生成SDD报告
                modelName = app.CurrentModelDropDown.Value;
                userName = getenv('username');
                result = app.ModelStaticAnalysisService.generateSDD(modelName, userName);
                
                app.updateStatusLights();
                
                close(d);
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台（规则第8条）
                
                app.callBackEnding(result.message);
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                msg = sprintf('生成SDD报告失败: %s', ME.message);
                app.callBackEnding(msg);
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台
            end
        end
        
        function OpenSDDButtonPushed(app, varargin)
            % 打开SDD报告
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保ModelStaticAnalysisService已初始化
                if isempty(app.ModelStaticAnalysisService)
                    app.ModelStaticAnalysisService = ModelStaticAnalysisService('Environment', 'DEV');
                end
                
                modelName = app.CurrentModelDropDown.Value;
                
                % 基于规则第4获取SDD报告路径并打开
                reportPath = app.ModelStaticAnalysisService.getSDDReportPath(modelName);
                
                if isempty(reportPath) || ~exist(reportPath, 'file')
                    uialert(app.MainFigure, sprintf('SDD报告文件不存在：%s\n请先生成SDD报告。', reportPath), ...
                        '文件未找到', 'Icon', 'warning');
                else
                    winopen(reportPath);
                    app.setMessage(sprintf('已打开SDD报告：%s', reportPath), 'success');
                end
                
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台（规则第8条）
                
            catch ME
                uialert(app.MainFigure, sprintf('打开SDD报告失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function OpenSLDDButtonPushed(app, varargin)
            % 打开SLDD文件 - 与mbdWorkflowTool_m保持一致
            try
                d = uiprogressdlg(app.MainFigure, ...
                    'Title', 'Please Wait', ...
                    'Message', 'Opening model data dictionary...', ...
                    'Indeterminate', 'on');
                
                mdlName = app.CurrentModelDropDown.Value;
                
                % 尝试使用项目约定获取数据字典名称
                try
                    mdlDDName = ProjectNamingManager.getNameStr('model data', mdlName);
                    uiopen([mdlDDName, '.sldd'], 1);
                catch
                    % 如果项目约定不可用，尝试使用模型名称
                    slddPath = [mdlName, '.sldd'];
                    if exist(slddPath, 'file')
                        uiopen(slddPath, 1);
                    else
                        uialert(app.MainFigure, '未找到数据字典文件', '提示', 'Icon', 'info');
                        % figure(app.MainFigure) #tmpfix;
                    end
                end
                
                close(d);

                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                uialert(app.MainFigure, sprintf('打开SLDD文件失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function ViewModelDependencyButtonPushed(app, varargin)
            % 查看模型依赖
            % 基于规则第8条：添加进度条
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Viewing Model Dependencies...', 'Indeterminate', 'on');
                
                % 确保ModelService已初始化（单例模式）
                if isempty(app.ModelService)
                    app.ModelService = ModelManagementService.getInstance();
                end
                
                app.ModelService.viewDependency(app.CurrentModelDropDown.Value, 'model');
                
                % 关闭进度对话框
                close(dlg);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure);
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                uialert(app.MainFigure, sprintf('查看模型依赖失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure);
            end
        end
        
        function ViewFileDependencyButtonPushed(app, varargin)
            % 查看文件依赖
            % 基于规则第8条：添加进度条
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Viewing File Dependencies...', 'Indeterminate', 'on');
                
                % 确保ModelService已初始化（单例模式）
                if isempty(app.ModelService)
                    app.ModelService = ModelManagementService.getInstance();
                end
                
                app.ModelService.viewDependency(app.CurrentModelDropDown.Value, 'file');
                
                % 关闭进度对话框
                close(dlg);
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                uialert(app.MainFigure, sprintf('查看文件依赖失败：%s', ME.message), '错误', 'Icon', 'error');
                figure(app.MainFigure);
            end
        end

        function RunAllButtonPushed(app, varargin)
            % 运行所有模型分析 - 依次执行Model Advisor、SDD报告、模型指标、SLDV检测
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 用户确认对话框 - 提示需要很长时间
                selection = uiconfirm(app.MainFigure, ...
                    ['将依次执行以下分析，这可能需要很长时间：' newline newline ...
                     '1. Model Advisor 标准检查' newline ...
                     '2. SDD 报告生成' newline ...
                     '3. 模型指标计算' newline ...
                     '4. SLDV 设计错误检测' newline newline ...
                     '是否继续执行？'], ...
                    '运行所有模型分析', ...
                    'Options', {'继续执行', '取消'}, ...
                    'DefaultOption', 2, ...
                    'CancelOption', 2, ...
                    'Icon', 'question');
                
                if strcmp(selection, '取消')
                    app.MessagesTextArea.Value = '用户取消了批量分析操作';
                    figure(app.MainFigure)
                    return;
                end
                
                % 开始执行所有分析
                app.MessagesTextArea.Value = '开始执行所有模型分析...';
                drawnow;
                
                analysisResults = {};  % 存储每个分析的结果
                
                % 1. 执行Model Advisor检查
                analysisResults{end+1} = app.runSingleAnalysis('Model Advisor', '正在运行Model Advisor...', @() app.RunModelAdvisorButtonPushed(), 1, 4, app.RunModelAdvisorButton);
                
                % 2. 执行SDD报告生成
                analysisResults{end+1} = app.runSingleAnalysis('SDD报告生成', '正在生成SDD报告...', @() app.SDDReportButtonPushed(), 2, 4, app.SDDReportButton);
                
                % 3. 执行模型指标计算
                analysisResults{end+1} = app.runSingleAnalysis('模型指标计算', '正在计算模型指标...', @() app.ComputeModelMetricsButtonPushed(), 3, 4, app.ComputeModelMetricsButton);
                
                % 4. 执行SLDV检测 (检查是否禁用)
                analysisResults{end+1} = app.runSingleAnalysis('SLDV设计错误检测', '正在运行SLDV检测...', @() app.RunSLDVButtonPushed(), 4, 4, app.RunSLDVButton);
                
                % 显示最终结果汇总
                app.showAnalysisSummary(analysisResults);
                
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                app.MessagesTextArea.Value = sprintf('批量分析执行失败: %s', ME.message);
                uialert(app.MainFigure, sprintf('批量分析执行失败：\n%s', ME.message), '错误', 'Icon', 'error');
                figure(app.MainFigure) 
            end
        end
        
        function result = runSingleAnalysis(app, analysisName, progressMessage, analysisFunction, currentStep, totalSteps, buttonHandle)
            % 执行单个分析，包含进度提示、步骤信息、计时和按钮状态检查
            result = struct('name', analysisName, 'success', false, 'message', '', 'duration', 0, 'skipped', false);
            
            % 检查按钮是否禁用（如果提供了buttonHandle参数）
            if nargin >= 7 && ~isempty(buttonHandle)
                if strcmp(buttonHandle.Enable, 'off')
                    result.message = sprintf('%s 已跳过（功能未启用）', analysisName);
                    result.skipped = true;
                    return;
                end
            end
            
            try
                % 记录开始时间
                startTime = tic;
                
                % 显示进度对话框，带步骤信息，放置在右下角避免遮挡
                stepInfo = sprintf('Step %d of %d', currentStep, totalSteps);
                initialMessage = sprintf('%s\n%s\n已用时间: 0 秒', stepInfo, progressMessage);
                
                % 创建主进度对话框，使用醒目标题区分
                d = uiprogressdlg(app.MainFigure, 'Title', '🚀 RunAll批量分析总进度 🚀', ...
                    'Message', initialMessage, 'Indeterminate', 'on');
                
                % 添加延时确保对话框完全创建后再尝试定位
                drawnow;
                pause(0.1);
                
                % 创建定时器，每秒更新已用时间
                timerObj = timer('ExecutionMode', 'fixedRate', 'Period', 1.0, ...
                    'TimerFcn', @(~,~) app.updateProgressDialog(d, stepInfo, progressMessage, startTime));
                start(timerObj);
                
                % 调用具体的分析函数
                analysisFunction();
                
                % 停止并删除定时器
                if isvalid(timerObj)
                    stop(timerObj);
                    delete(timerObj);
                end
                
                % 记录执行时间
                result.duration = toc(startTime);
                result.success = true;
                result.message = sprintf('%s 完成 (耗时: %.1f 秒)', analysisName, result.duration);
                
                close(d);
                
            catch ME
                % 清理定时器
                if exist('timerObj', 'var') && isvalid(timerObj)
                    stop(timerObj);
                    delete(timerObj);
                end
                
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                
                result.duration = toc(startTime);
                result.success = false;
                result.message = sprintf('%s 失败: %s (耗时: %.1f 秒)', analysisName, ME.message, result.duration);
                
                % 记录错误但继续执行后续分析
                app.MessagesTextArea.Value = result.message;
                drawnow;
            end
        end
        
        function updateProgressDialog(app, progressDlg, stepInfo, progressMessage, startTime)
            % 更新进度对话框中的计时信息
            try
                if isvalid(progressDlg)
                    elapsedTime = toc(startTime);
                    updatedMessage = sprintf('%s\n%s\n已用时间: %.0f 秒', stepInfo, progressMessage, elapsedTime);
                    progressDlg.Message = updatedMessage;
                end
            catch
                % 如果更新失败，静默忽略
            end
        end
        
        function showAnalysisSummary(app, results)
            % 显示分析结果汇总，包含跳过项的统计
            % 将cell数组转换为结构体数组以支持点索引
            if iscell(results)
                results = [results{:}];
            end
            successCount = sum([results.success]);
            
            % 检查是否有skipped字段
            if isfield(results, 'skipped')
                skippedCount = sum([results.skipped]);
            else
                skippedCount = 0;
            end
            
            totalCount = length(results);
            failedCount = totalCount - successCount - skippedCount;
            
            % 计算总耗时
            totalDuration = sum([results.duration]);
            
            if skippedCount > 0
                summaryMessage = sprintf('批量分析完成！成功：%d，跳过：%d，失败：%d，总耗时：%.1f 秒\n\n', ...
                    successCount, skippedCount, failedCount, totalDuration);
            else
                summaryMessage = sprintf('批量分析完成！成功：%d/%d，总耗时：%.1f 秒\n\n', ...
                    successCount, totalCount, totalDuration);
            end
            
            for i = 1:length(results)
                if isfield(results(i), 'skipped') && results(i).skipped
                    status = '⏭️';
                elseif results(i).success
                    status = '✅';
                else
                    status = '❌';
                end
                summaryMessage = [summaryMessage sprintf('%s %s\n', status, results(i).message)];
            end
            
            app.MessagesTextArea.Value = summaryMessage;
            
            % 显示汇总对话框
            if failedCount == 0
                if skippedCount == 0
                    icon = 'success';
                    title = '分析完成';
                else
                    icon = 'info';
                    title = '分析完成（部分跳过）';
                end
            elseif successCount > 0
                icon = 'warning';
                title = '部分完成';
            else
                icon = 'error';
                title = '分析失败';
            end
            
            uialert(app.MainFigure, summaryMessage, title, 'Icon', icon);
        end
        
        % === Model Analysis Tab Callbacks ===
        function OpenModelAdvisorButtonPushed(app, varargin)
            % 打开Model Advisor - 调用openMdlAdv函数，与mbdWorkflowTool_m保持一致
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                mdlName = app.CurrentModelDropDown.Value;
                d = uiprogressdlg(app.MainFigure, ...
                    'Title', 'Please Wait', ...
                    'Message', ['Opening model advisor for model ', mdlName, '...'], ...
                    'Indeterminate', 'on');
                
                openMdlAdv(mdlName);
                
                close(d);
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                uialert(app.MainFigure, sprintf('打开Model Advisor失败：%s', ME.message), '错误', 'Icon', 'error');
                % % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function RunModelAdvisorButtonPushed(app, varargin)
            % 运行Model Advisor - 基于规则第4调用ModelStaticAnalysisService（2025-10-14重构）
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保ModelStaticAnalysisService已初始化
                if isempty(app.ModelStaticAnalysisService)
                    app.ModelStaticAnalysisService = ModelStaticAnalysisService('Environment', 'DEV');
                end
                
                app.startLogging();
                d = uiprogressdlg(app.MainFigure, ...
                    'Title', '🔍 Model Advisor Analysis', ...
                    'Message', 'Running model advisor...', ...
                    'Indeterminate', 'on');
                
                toWorkFolder;
                mdlName = app.CurrentModelDropDown.Value;
                
                % 基于规则第4调用ModelStaticAnalysisService检查模型标准
                % checkStandards 不支持 TreatAsTopMdl 参数（该参数仅用于 computeMetrics）
                result = app.ModelStaticAnalysisService.checkStandards(mdlName);
                
                app.updateStatusLights();
                msg = result.message;
                
                close(d);
                figure(app.MainFigure);
                
                app.callBackEnding(msg);
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                msg = ['运行Model Advisor失败: ', ME.message];
                app.callBackEnding(msg);
                figure(app.MainFigure);
            end
        end
        
        function OpenReportButtonPushed(app, varargin)
            % 打开Model Standards报告
            % 基于规则第8条：添加进度条
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Report...', 'Indeterminate', 'on');
                
                app.openReport('model standards');
                
                % 关闭进度对话框
                close(dlg);
                
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                uialert(app.MainFigure, sprintf('打开报告失败：%s', ME.message), '错误', 'Icon', 'error');
                figure(app.MainFigure);
            end
        end
        
        function ComputeModelMetricsButtonPushed(app, varargin)
            % 计算模型指标 - 基于规则第4调用ModelStaticAnalysisService（2025-10-14重构）
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保ModelStaticAnalysisService已初始化
                if isempty(app.ModelStaticAnalysisService)
                    app.ModelStaticAnalysisService = ModelStaticAnalysisService('Environment', 'DEV');
                end
                
                app.startLogging();
                d = uiprogressdlg(app.MainFigure, ...
                    'Title', '📊 Model Metrics Computation', ...
                    'Message', 'Computing model metrics...', ...
                    'Indeterminate', 'on');
                
                toWorkFolder;
                mdlName = app.CurrentModelDropDown.Value;
                
                % 基于规则第4调用ModelStaticAnalysisService计算模型指标
                if app.TopModelCheckBox.Value
                    result = app.ModelStaticAnalysisService.computeMetrics(mdlName, 'TreatAsTopMdl', true);
                else
                    result = app.ModelStaticAnalysisService.computeMetrics(mdlName);
                end
                
                app.updateStatusLights();
                msg = result.message;
                
                close(d);
                figure(app.MainFigure);
                
                app.callBackEnding(msg);
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                msg = sprintf('计算模型指标失败: %s', ME.message);
                app.callBackEnding(msg);
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function ModelMetricsOpenReportButtonPushed(app, varargin)
            % 打开Model Metrics报告
            % 基于规则第8条：添加进度条
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Report...', 'Indeterminate', 'on');
                
                app.openReport('model metrics');
                
                % 关闭进度对话框
                close(dlg);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                uialert(app.MainFigure, sprintf('打开报告失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function OpenSLDVButtonPushed(app, varargin)
            % 打开SLDV配置界面
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                modelName = app.CurrentModelDropDown.Value;
                
                % 基于规则第4条：调用现有的工具函数
                openSLDVOpt(modelName);
                
                app.setMessage(sprintf('已打开SLDV配置界面：%s', modelName), 'success');
                
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台（规则第8条）
                
            catch ME
                uialert(app.MainFigure, sprintf('打开SLDV配置失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        
        function SLDVOpenReportButtonPushed(app, varargin)
            % 打开SLDV报告
            % 基于规则第8条：添加进度条
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Report...', 'Indeterminate', 'on');
                
                app.openReport('design error detection');
                
                % 关闭进度对话框
                close(dlg);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                uialert(app.MainFigure, sprintf('打开报告失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        
        % Menu Callbacks
        function UserGuideMenuSelected(app, varargin)
            % 打开用户指南
            uialert(app.MainFigure, '用户指南功能待实现', '信息', 'Icon', 'info');
        end
        
        function AboutMenuSelected(app, varargin)
            message = 'MBD Requirements-Driven Platform v2.0\n基于Grid Layout的现代化工作流工具';
            uialert(app.MainFigure, sprintf(message), 'About', 'Icon', 'info');
        end
        
        % Main Control Callbacks
        function SwitchtoworkfolderButtonPushed(app, varargin)
            % 切换到工作文件夹 - 调用toWorkFolder，与mbdWorkflowTool_m保持一致
            try
                toWorkFolder;
                app.callBackEnding('已切换到工作目录');
                
            catch ME
                msg = sprintf('切换目录失败: %s', ME.message);
                app.callBackEnding(msg);
            end
        end
        
        function OpenICDButtonPushed(app, varargin)
            % 打开ICD Excel文件按钮回调 - 调用独立的业务逻辑函数
            [success, errorMsg] = openICD();
            
            if success
                app.callBackEnding('已成功打开ICD文件');
            else
                app.callBackEnding(['打开ICD失败: ' errorMsg]);
                uialert(app.MainFigure, errorMsg, 'ICD操作错误', 'Icon', 'warning');
            end
            
            % 确保主窗口回到前台（规则第8条）
            % figure(app.MainFigure) #tmpfix;
        end
        
        function OpenSystemArchitectureButtonPushed(app, varargin)
            % 打开系统架构文档按钮回调 - 调用独立的业务逻辑函数
            [success, errorMsg] = openSystemArchitecture();
            
            if success
                app.callBackEnding('已成功打开系统架构文档');
            else
                app.callBackEnding(['打开系统架构文档失败: ' errorMsg]);
                uialert(app.MainFigure, errorMsg, '系统架构文档操作错误', 'Icon', 'warning');
            end

            % 确保主窗口回到前台（规则第8条）
            % figure(app.MainFigure) #tmpfix;
        end
        
        function OpenProjectDocArchitectureButtonPushed(app, varargin)
            % 打开项目文档架构图按钮回调 - 调用独立的业务逻辑函数
            [success, errorMsg] = openProjectDocumentArchitecture();
            
            if success
                app.callBackEnding('已成功打开项目文档架构图');
            else
                app.callBackEnding(['打开项目文档架构图失败: ' errorMsg]);
                uialert(app.MainFigure, errorMsg, '项目文档架构图操作错误', 'Icon', 'warning');
            end

            % 确保主窗口回到前台（规则第8条）
            % figure(app.MainFigure) #tmpfix;
        end
        
        function RefreshModelButtonPushed(app, varargin)
            % 刷新模型列表按钮 - 与mbdWorkflowTool_m保持一致
            d = uiprogressdlg(app.MainFigure, ...
                'Title', 'Please wait', ...
                'Message', 'Refreshing model directory...', ...
                'Indeterminate', 'on');
            
            try
                % 刷新设置页面（模型列表和树）- 不显示内部进度条，使用外部统一进度条
                app.refreshModelList(false); % 不显示内部进度条
                app.refreshModelTree(false); % 不显示内部进度条
                
                app.callBackEnding('model directory reflashed.');
                close(d);
                figure(app.MainFigure); % 确保主窗口在前台
            catch ME
                % 关闭进度条
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                app.callBackEnding(['刷新失败: ' ME.message]);
                uialert(app.MainFigure, ME.message, '刷新错误', 'Icon', 'error');
                figure(app.MainFigure);
            end
        end
        
        function CurrentModelDropDownValueChanged(app, varargin)
            % 当前模型变更
            % 更新树中的高亮显示
            app.highlightCurrentModelInTree();
            
            % 如果当前在Testing Management标签页，刷新测试用例表格和状态灯
            if isequal(app.SubTabGroup.SelectedTab, app.TestingTab)
                app.loadTestCases();
                app.refreshTestReportLamps();
            end
        end
        
        function TopOpenModelButtonPushed(app, varargin)
            % 顶部打开模型按钮回调 - 全局快捷打开模型功能
            % 基于规则第4调用ModelManagementService
            try
                currentModel = app.CurrentModelDropDown.Value;
                
                % 确保ModelService已初始化（单例模式）
                if isempty(app.ModelService)
                    app.ModelService = ModelManagementService.getInstance();
                end
                
                % 验证模型名称
                if ~app.ModelService.validateModelName(currentModel)
                    uialert(app.MainFigure, ...
                        'Please select a valid model first.', ...
                        'No Model Selected', 'Icon', 'warning');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 显示进度对话框（基于规则第8条）
                d = uiprogressdlg(app.MainFigure, ...
                    'Title', 'Opening Model', ...
                    'Message', ['Opening ' currentModel '...'], ...
                    'Indeterminate', 'on');
                
                % 调用ModelService打开模型
                result = app.ModelService.openModel(currentModel);
                
                app.MessagesTextArea.Value = result.message;
                close(d);
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台（规则第8条）
                
            catch ME
                if exist('d', 'var')
                    close(d);
                end
                uialert(app.MainFigure, ...
                    ['Failed to open model: ' ME.message], ...
                    'Open Model Error', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        
        
        
        % Model Management Tab Callbacks
        
        function RefreshTreeButtonPushed(app, varargin)
            % 刷新模型树按钮回调
            try
                if ~isempty(app.logger)
                    app.logger.logInfo('用户点击刷新模型树按钮');
                end
                
                app.MessagesTextArea.Value = '正在刷新模型树...';
                drawnow;
                
                % 刷新模型树和模型列表
                app.refreshModelTree();
                app.refreshModelList();
                
                app.MessagesTextArea.Value = '模型树刷新完成';
                
            catch ME
                app.MessagesTextArea.Value = sprintf('模型树刷新失败: %s', ME.message);
                uialert(app.MainFigure, sprintf('模型树刷新失败：\n%s', ME.message), '错误', 'Icon', 'error');
            end
        end
        
        function SelectModelButtonPushed(app, varargin)
            % 选择模型按钮回调
            % 基于规则第4使用ModelManagementService验证
            if isempty(app.ModelTree.SelectedNodes) || strcmp(app.ModelTree.SelectedNodes.Text, 'Design Models')
                uialert(app.MainFigure, ...
                    'Please select a model (not the root node) in the tree first.', ...
                    'MBD Platform', 'Icon', 'warning');
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台（规则第8条）
                return;
            end
                
            % 获取选中节点的文本，并提取原始模型名（移除高亮格式）
            selectedNodeText = app.ModelTree.SelectedNodes.Text;
            
            % 提取原始模型名：如果有高亮格式，则提取★★★【和】★★★之间的内容
            if startsWith(selectedNodeText, '★★★【') && endsWith(selectedNodeText, '】★★★')
                % 提取原始模型名称
                originalName = extractBetween(selectedNodeText, '★★★【', '】★★★');
                if ~isempty(originalName)
                    selectedNodeText = originalName{1};
                end
            end
            
            % 检查是否选择了"(not on path)"节点
            if contains(selectedNodeText, '(not on path)')
                uialert(app.MainFigure, ...
                    ['Cannot set "' selectedNodeText '" as current model because it is not on MATLAB path. ' ...
                     'Please add the model to the MATLAB path first or select a different model.'], ...
                    'MBD Platform', 'Icon', 'warning');
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台
                return;
            end
            
            % 确保ModelService已初始化
            if isempty(app.ModelService)
                app.ModelService = ModelManagementService();
            end
            
            % 使用ModelService验证模型名称
            if ~app.ModelService.validateModelName(selectedNodeText)
                uialert(app.MainFigure, ...
                    sprintf('Invalid model name: %s', selectedNodeText), ...
                    'MBD Platform', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
                return;
            end
                
            % 设置当前模型
            app.CurrentModelDropDown.Value = selectedNodeText;
            
            % 确定是否为顶层模型
            % Design Models (根节点)
            % ├── TopModel1 ✓ (直接子节点 = 顶层模型)
            % ├── TopModel2 ✓ (直接子节点 = 顶层模型)
            % └── TopModel3 ✓ (直接子节点 = 顶层模型)
            %     └── SubModel ✗ (孙节点 ≠ 顶层模型)
            if ~strcmp(app.ModelTree.SelectedNodes.Text, 'Design Models') && ...
                    strcmp(app.ModelTree.SelectedNodes.Parent.Text, 'Design Models')
                app.TopModelCheckBox.Value = true;
            else
                app.TopModelCheckBox.Value = false;
            end
            
            % 刷新状态指示灯
            app.updateStatusLights();
            
            % 更新树中的高亮显示
            app.highlightCurrentModelInTree();
            
            % 结束回调
            app.callBackEnding(['current model set to ' app.CurrentModelDropDown.Value '.']);
        end
        
        function OpenModelButtonPushed(app, varargin)
            % 打开模型按钮回调 - 基于规则第4使用ModelManagementService
            d = uiprogressdlg(app.MainFigure, ...
                'Title', 'Please wait', ...
                'Message', 'Opening model...', ...
                'Indeterminate', 'on');
            
            try
                % 确保ModelService已初始化（单例模式）
                if isempty(app.ModelService)
                    app.ModelService = ModelManagementService.getInstance();
                end
                
                % 使用ModelManagementService打开模型
                result = app.ModelService.openModel(app.CurrentModelDropDown.Value);
                
                if ~result.success
                    uialert(app.MainFigure, result.message, '打开模型失败', 'Icon', 'error');
                end
                
            catch ME
                uialert(app.MainFigure, sprintf('打开模型失败: %s', ME.message), '错误', 'Icon', 'error');
            end
            
            close(d);
            % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台
        end
        
        function DeleteModelButtonPushed(app, varargin)
            % 删除模型按钮回调
            % 基于规则第4调用ModelManagementService
            try
                if isempty(app.ModelTree.SelectedNodes)
                    uialert(app.MainFigure, '请先选择要删除的模型', '提示', 'Icon', 'warning');
                    figure(app.MainFigure);
                    return;
                end
                
                % 获取选中的模型
                selectedNodes = app.ModelTree.SelectedNodes;
                model2delete = {};
                for i = 1:length(selectedNodes)
                    if ~strcmp(selectedNodes(i).Text, 'Design Models')
                        model2delete{end+1} = selectedNodes(i).Text;
                    end
                end
        
                if isempty(model2delete)
                    uialert(app.MainFigure, '请选择要删除的具体模型', '提示', 'Icon', 'info');
                    figure(app.MainFigure) 
                    return;
                end
                
                numOfModel2delete = numel(model2delete);
                
                % 先确认是否删除
                selection = uiconfirm(app.MainFigure, ...
                    sprintf('Are you sure to delete model %s and related folders?', strjoin(model2delete, ' , ')), ...
                    'confirm delete', 'Icon', 'warning');
                % figure(app.MainFigure) #tmpfix;
                
                if strcmp(selection, 'Cancel')
                    return;
                end
                
                % 用户确认删除后，如果是多模型删除，显示处理提示
                if numOfModel2delete > 1
                    uialert(app.MainFigure, ...
                        sprintf('Processing deletion of %d models. Please wait...', numOfModel2delete), ...
                        'Multiple model deletion', 'Icon', 'info');
                    % figure(app.MainFigure) #tmpfix;
                end
                
                % 显示进度条（基于规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', 'Please wait', ...
                    'Message', 'Deleting model(s)...', 'Indeterminate', 'on');
                
                app.startLogging();
                
                % 确保ModelService已初始化（单例模式）
                if isempty(app.ModelService)
                    app.ModelService = ModelManagementService.getInstance();
                end
                
                % 调用ModelService批量删除模型
                result = app.ModelService.deleteModel(model2delete);
                
                % 刷新设置页面 - 不显示内部进度条，使用外部统一进度条
                app.refreshModelList(false); % 不显示内部进度条
                app.refreshModelTree(false); % 不显示内部进度条
                
                close(d);
                figure(app.MainFigure); % 确保主窗口在前台（规则第8条）
                
                app.callBackEnding(result.message);
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                msg = sprintf('删除模型失败: %s', ME.message);
                app.callBackEnding(msg);
                figure(app.MainFigure)
            end
        end
        
        function ModelTreeSelectionChanged(app, varargin)
            % 模型树选择变化回调
            try
                selectedNodes = app.ModelTree.SelectedNodes;
                
                if isempty(selectedNodes)
                    % 没有选择
                    app.SelectModelButton.Enable = 'off';
                    app.OpenModelButton.Enable = 'off';
                    app.DeleteModelButton.Enable = 'off';
                elseif length(selectedNodes) == 1
                    % 选择了一个节点
                    if strcmp(selectedNodes(1).Text, 'Design Models')
                        % 选择了根节点
                        app.SelectModelButton.Enable = 'off';
                        app.OpenModelButton.Enable = 'off';
                        app.DeleteModelButton.Enable = 'off';
                    else
                        % 选择了模型节点
                        app.SelectModelButton.Enable = 'on';
                        app.OpenModelButton.Enable = 'on';
                        app.DeleteModelButton.Enable = 'on';
                    end
                else
                    % 多选
                    app.SelectModelButton.Enable = 'off';  % 多选时不能设为当前模型
                    app.OpenModelButton.Enable = 'off';    % 多选时不能打开
                    app.DeleteModelButton.Enable = 'on';   % 支持批量删除
                end
                
            catch ME
                % 选择变化回调中的错误不应该中断用户操作
                fprintf('模型树选择变化处理错误: %s\n', ME.message);
            end
        end
        
        function RunSLDVButtonPushed(app, varargin)
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                app.MessagesTextArea.Value = '正在运行SLDV设计缺陷检测...';
                drawnow;
                
                % 显示进度条
                d = uiprogressdlg(app.MainFigure, 'Title', '🔍 SLDV Design Error Detection', ...
                    'Message', '正在检测设计缺陷...', 'Indeterminate', 'on');
                
                mdlName = app.CurrentModelDropDown.Value;
                % 基于规则第4通过Service Layer调用，而非直接调用底层工具函数
                result = app.ModelStaticAnalysisService.detectDesignErrors(mdlName);
                
                app.MessagesTextArea.Value = result.message;
                
                % 更新状态灯
                app.updateStatusLights();
                figure(app.MainFigure)
                close(d);
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                figure(app.MainFigure)
                app.MessagesTextArea.Value = sprintf('SLDV检测失败: %s', ME.message);
                uialert(app.MainFigure, sprintf('SLDV设计缺陷检测失败：\n%s', ME.message), '错误', 'Icon', 'error');
            end
        end

        % Code Generation Tab Callbacks
        function GenerateCodeButtonPushed(app, varargin)
            % 代码生成
            % 基于规则第4调用CodeAnalysisService
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保CodeService已初始化
                if isempty(app.CodeService)
                    app.CodeService = CodeAnalysisService.getInstance();
                end
                
                % 显示进度条（规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                    'Message', '正在使用Embedded Coder生成C/C++代码...', 'Indeterminate', 'on');
                
                modelName = app.CurrentModelDropDown.Value;
                
                % 调用CodeService生成代码
                % 基于规则第4调用CodeAnalysisService，根据TopModelCheckBox决定是否传递TreatAsTopMdl参数
                if app.TopModelCheckBox.Value
                    result = app.CodeService.generateCode(modelName, 'TreatAsTopMdl', true);
                else
                    result = app.CodeService.generateCode(modelName);
                end
                
                app.MessagesTextArea.Value = result.message;
                
                % 更新状态灯
                app.updateStatusLights();
                
                close(d);
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                app.MessagesTextArea.Value = sprintf('代码生成失败: %s', ME.message);
                uialert(app.MainFigure, sprintf('代码生成失败：\n%s', ME.message), '错误', 'Icon', 'error');
            end
        end
        
        function OpenCodeReportButtonPushed(app, varargin)
            % 打开代码生成报告 - 参考旧版实现
            % 基于规则第8条：添加进度条
            if ~app.validateCurrentModel()
                return;
            end
            
            mdlName = app.CurrentModelDropDown.Value;
            try
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Report...', 'Indeterminate', 'on');
                
                if app.TopModelCheckBox.Value
                    codeRptPath = getRptPath('code as top', mdlName);
                else
                    codeRptPath = getRptPath('code as ref', mdlName);
                end
                
                if exist(codeRptPath, 'file')
                    winopen(codeRptPath);
                    app.MessagesTextArea.Value = sprintf('已打开代码生成报告：%s', codeRptPath);
                else
                    close(dlg);
                    uialert(app.MainFigure, sprintf('代码生成报告不存在：%s\n请先生成代码。', codeRptPath), '警告', 'Icon', 'warning');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 关闭进度对话框
                close(dlg);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                uialert(app.MainFigure, sprintf('打开代码生成报告失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function OpenCodeInspectorReportButtonPushed(app, varargin)
            % 打开代码检查报告
            % 基于规则第8条：添加进度条
            if ~app.validateCurrentModel()
                return;
            end
            
            mdlName = app.CurrentModelDropDown.Value;
            try
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Report...', 'Indeterminate', 'on');
                
                reportPath = getRptPath('code inspector', mdlName);
                
                if exist(reportPath, 'file')
                    web(reportPath, '-browser');
                    app.MessagesTextArea.Value = sprintf('已打开代码检查报告：%s', reportPath);
                else
                    close(dlg);
                    uialert(app.MainFigure, sprintf('代码检查报告不存在：%s\n请先运行Code Inspector。', reportPath), '警告', 'Icon', 'warning');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 关闭进度对话框
                close(dlg);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                uialert(app.MainFigure, sprintf('打开代码检查报告失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function OpenBugFinderReportButtonPushed(app, varargin)
            % 打开Bug Finder报告
            % 基于规则第8条：添加进度条
            if ~app.validateCurrentModel()
                return;
            end
            
            mdlName = app.CurrentModelDropDown.Value;
            try
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Report...', 'Indeterminate', 'on');
                
                reportPath = getRptPath('bug finder', mdlName);
                
                if exist(reportPath, 'file')
                    web(reportPath, '-browser');
                    app.MessagesTextArea.Value = sprintf('已打开Bug Finder报告：%s', reportPath);
                else
                    close(dlg);
                    uialert(app.MainFigure, sprintf('Bug Finder报告不存在：%s\n请先运行Bug Finder。', reportPath), '警告', 'Icon', 'warning');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 关闭进度对话框
                close(dlg);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                uialert(app.MainFigure, sprintf('打开Bug Finder报告失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function OpenCodeStandardsReportButtonPushed(app, varargin)
            % 打开Code Standards报告
            % 基于规则第8条：添加进度条
            if ~app.validateCurrentModel()
                return;
            end
            
            mdlName = app.CurrentModelDropDown.Value;
            try
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Report...', 'Indeterminate', 'on');
                
                reportPath = getRptPath('code standards', mdlName);
                
                if exist(reportPath, 'file')
                    web(reportPath, '-browser');
                    app.MessagesTextArea.Value = sprintf('已打开Code Standards报告：%s', reportPath);
                else
                    close(dlg);
                    uialert(app.MainFigure, sprintf('Code Standards报告不存在：%s\n请先运行Code Standards检查。', reportPath), '警告', 'Icon', 'warning');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 关闭进度对话框
                close(dlg);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                uialert(app.MainFigure, sprintf('打开Code Standards报告失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function OpenCodeProverReportButtonPushed(app, varargin)
            % 打开Code Prover报告
            % 基于规则第8条：添加进度条
            if ~app.validateCurrentModel()
                return;
            end
            
            mdlName = app.CurrentModelDropDown.Value;
            try
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Report...', 'Indeterminate', 'on');
                
                reportPath = getRptPath('code prover', mdlName);
                
                if exist(reportPath, 'file')
                    web(reportPath, '-browser');
                    app.MessagesTextArea.Value = sprintf('已打开Code Prover报告：%s', reportPath);
                else
                    close(dlg);
                    uialert(app.MainFigure, sprintf('Code Prover报告不存在：%s\n请先运行Code Prover。', reportPath), '警告', 'Icon', 'warning');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 关闭进度对话框
                close(dlg);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                uialert(app.MainFigure, sprintf('打开Code Prover报告失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function OpenCodeMetricsReportButtonPushed(app, varargin)
            % 打开Code Metrics报告
            if ~app.validateCurrentModel()
                return;
            end
            
            mdlName = app.CurrentModelDropDown.Value;
            try
                reportPath = getRptPath('code metrics', mdlName);
                
                if exist(reportPath, 'file')
                    web(reportPath, '-browser');
                    app.MessagesTextArea.Value = sprintf('已打开Code Metrics报告：%s', reportPath);
                else
                    uialert(app.MainFigure, sprintf('Code Metrics报告不存在：%s\n请先运行Code Metrics计算。', reportPath), '警告', 'Icon', 'warning');
                end
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                uialert(app.MainFigure, sprintf('打开Code Metrics报告失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function RunCodeInspectorButtonPushed(app, varargin)
            % 运行代码检查器
            % 基于规则第4调用CodeAnalysisService
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保CodeService已初始化
                if isempty(app.CodeService)
                    app.CodeService = CodeAnalysisService.getInstance();
                end
                
                % 显示进度条（规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                    'Message', '正在验证生成代码与模型一致性...', 'Indeterminate', 'on');
                
                modelName = app.CurrentModelDropDown.Value;
                
                % 调用CodeService运行代码检查
                % 基于规则第4调用CodeAnalysisService，根据TopModelCheckBox决定是否传递TreatAsTopMdl参数
                if app.TopModelCheckBox.Value
                    result = app.CodeService.runCodeInspector(modelName, 'TreatAsTopMdl');
                else
                    result = app.CodeService.runCodeInspector(modelName);
                end
                
                app.MessagesTextArea.Value = result.message;
                
                % 更新状态灯
                app.updateStatusLights();
                
                close(d);
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                app.MessagesTextArea.Value = sprintf('代码检查失败: %s', ME.message);
                uialert(app.MainFigure, sprintf('代码检查失败：\n%s', ME.message), '错误', 'Icon', 'error');
            end
        end
        
        function ComputeCodeMetricsButtonPushed(app, varargin)
            % 计算代码指标
            % 基于规则第4调用CodeAnalysisService
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保CodeService已初始化
                if isempty(app.CodeService)
                    app.CodeService = CodeAnalysisService.getInstance();
                end
                
                % 显示进度条（规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                    'Message', '正在分析代码复杂度、行数、函数统计...', 'Indeterminate', 'on');
                
                modelName = app.CurrentModelDropDown.Value;
                
                % 调用CodeService计算代码指标
                % 基于规则第4调用CodeAnalysisService，根据TopModelCheckBox决定是否传递TreatAsTopMdl参数
                if app.TopModelCheckBox.Value
                    result = app.CodeService.computeCodeMetrics(modelName, 'TreatAsTopMdl');
                else
                    result = app.CodeService.computeCodeMetrics(modelName);
                end
                
                app.MessagesTextArea.Value = result.message;
                
                % 更新状态灯
                app.updateStatusLights();
                
                close(d);
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                app.MessagesTextArea.Value = sprintf('代码指标计算失败: %s', ME.message);
                uialert(app.MainFigure, sprintf('代码指标计算失败：\n%s', ME.message), '错误', 'Icon', 'error');
            end
        end
        
        % Testing Tab Callbacks
        function SILTestButtonPushed(app, varargin)
            app.MessagesTextArea.Value = '运行SIL测试：软件在环验证';
        end
        
        function RunHLRTestButtonPushed(app, varargin)
            % HLR测试
            % 基于规则第4调用TestManagementService
            testMode = app.TestModeDropDown.Value;
            app.runTestInMode('HLR', testMode);
            
            % 测试完成后，如果在Testing Management标签页，刷新测试用例表格
            if isequal(app.SubTabGroup.SelectedTab, app.TestingTab)
                app.loadTestCases();
            end
        end
        
        function RunLLRTestButtonPushed(app, varargin)
            % LLR测试
            % 基于规则第4调用TestManagementService
            testMode = app.TestModeDropDown.Value;
            app.runTestInMode('LLR', testMode);
        end
        
        function runTestInMode(app, testType, testMode)
            % 统一的测试执行逻辑
            % 基于规则第4调用TestManagementService
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保TestService已初始化
                if isempty(app.TestService)
                    app.TestService = TestManagementService.getInstance();
                end
                
                mdlName = app.CurrentModelDropDown.Value;
                app.MessagesTextArea.Value = sprintf('正在执行%s %s测试...', testType, testMode);
                drawnow;
                
                % 显示进度条（规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                    'Message', sprintf('正在运行%s %s测试...', testType, testMode), 'Indeterminate', 'on');
                
                % 调用TestService运行测试
                result = app.TestService.runTest(mdlName, testType, testMode);
                
                % 更新消息
                app.MessagesTextArea.Value = result.message;
                
                % 更新状态灯
                if result.success
                    switch testType
                        case 'HLR'
                            app.HLRTestLamp.Color = 'green';
                        case 'LLR'
                            app.LLRTestLamp.Color = 'green';
                    end
                else
                    switch testType
                        case 'HLR'
                            app.HLRTestLamp.Color = 'red';
                        case 'LLR'
                            app.LLRTestLamp.Color = 'red';
                    end
                end
                
                close(d);
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台（规则第8条）
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                errorMsg = sprintf('%s %s测试失败: %s', testType, testMode, ME.message);
                app.MessagesTextArea.Value = errorMsg;
                uialert(app.MainFigure, sprintf('%s %s测试运行失败：\n%s', testType, testMode, ME.message), '错误', 'Icon', 'error');
                
                switch testType
                    case 'HLR'
                        app.HLRTestLamp.Color = 'red';
                    case 'LLR'
                        app.LLRTestLamp.Color = 'red';
                end
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function MergeCoverageButtonPushed(app, varargin)
            app.MessagesTextArea.Value = '合并覆盖率：整合HLR、LLR测试覆盖率数据';
            app.CoverageGauge.Text = '85%'; % 示例值
        end
        
        % Reports Tab Callbacks
        function GenerateTestReportButtonPushed(app, varargin)
            % TODO (2025-10-14): 测试报告生成功能需要重新实现
            % 原ReportGenerationService已被删除，此功能需要分配到TestManagementService或使用Test Manager API
            % 基于规则第2明确报告错误而不是silent failure
            
            uialert(app.MainFigure, ...
                ['测试报告生成功能正在重构中。', newline, newline, ...
                 '临时解决方案：', newline, ...
                 '1. 使用 Simulink Test Manager 手动生成测试报告', newline, ...
                 '2. 或等待功能重新实现完成'], ...
                '功能暂不可用', 'Icon', 'warning');
            
            app.MessagesTextArea.Value = '⚠️ 测试报告生成功能正在重构中，请使用Test Manager手动生成报告';
            % figure(app.MainFigure) #tmpfix;
        end
        
        function GenerateCoverageReportButtonPushed(app, varargin)
            % TODO (2025-10-14): 覆盖率报告生成功能需要重新实现
            % 原ReportGenerationService已被删除，此功能需要分配到TestManagementService或使用Coverage API
            % 基于规则第2明确报告错误而不是silent failure
            
            uialert(app.MainFigure, ...
                ['覆盖率报告生成功能正在重构中。', newline, newline, ...
                 '临时解决方案：', newline, ...
                 '1. 使用 Simulink Coverage 工具手动生成覆盖率报告', newline, ...
                 '2. 或等待功能重新实现完成'], ...
                '功能暂不可用', 'Icon', 'warning');
            
            app.MessagesTextArea.Value = '⚠️ 覆盖率报告生成功能正在重构中，请使用Coverage工具手动生成报告';
            % figure(app.MainFigure) #tmpfix;
        end
        
        function GenerateSDDButtonPushed(app, varargin)
            % 生成SDD报告（Reports标签页版本）
            % 基于规则第4调用ModelStaticAnalysisService（2025-10-14重构）
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保ModelStaticAnalysisService已初始化
                if isempty(app.ModelStaticAnalysisService)
                    app.ModelStaticAnalysisService = ModelStaticAnalysisService('Environment', 'DEV');
                end
                
                app.MessagesTextArea.Value = '正在生成SDD报告...';
                drawnow;
                
                % 显示进度条（规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                    'Message', '正在生成系统设计描述文档...', 'Indeterminate', 'on');
                
                mdlName = app.CurrentModelDropDown.Value;
                currentUser = app.getCurrentUserName(); % 获取当前用户名
                
                % 基于规则第4调用ModelStaticAnalysisService生成SDD报告
                result = app.ModelStaticAnalysisService.generateSDD(mdlName, currentUser);
                
                app.MessagesTextArea.Value = result.message;
                
                close(d);
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台（规则第8条）
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                app.MessagesTextArea.Value = sprintf('SDD报告生成失败: %s', ME.message);
                uialert(app.MainFigure, sprintf('SDD报告生成失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function ExportDeliverableButtonPushed(app, varargin)
            % TODO (2025-10-14): DO-178C交付物导出功能需要重新实现
            % 原ReportGenerationService已被删除，此功能需要重新设计和实现
            % 基于规则第2明确报告错误而不是silent failure
            
            uialert(app.MainFigure, ...
                ['DO-178C交付物导出功能正在重构中。', newline, newline, ...
                 '临时解决方案：', newline, ...
                 '1. 手动收集各项报告和文档', newline, ...
                 '2. 参考DO_05_Artifacts目录结构', newline, ...
                 '3. 或等待功能重新实现完成'], ...
                '功能暂不可用', 'Icon', 'warning');
            
            app.MessagesTextArea.Value = '⚠️ DO-178C交付物导出功能正在重构中';
            % figure(app.MainFigure) #tmpfix;
        end
        
        function PILTestButtonPushed(app, varargin)
            app.MessagesTextArea.Value = '运行PIL测试：处理器在环验证（需要硬件支持）';
        end
        
        function RunBugFinderButtonPushed(app, varargin)
            % 运行Polyspace Bug Finder - 检测编码错误
            % 基于规则第4调用CodeAnalysisService
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保CodeService已初始化
                if isempty(app.CodeService)
                    app.CodeService = CodeAnalysisService.getInstance();
                end
                
                % 显示进度条（规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                    'Message', '正在运行Polyspace Bug Finder...', 'Indeterminate', 'on');
                
                modelName = app.CurrentModelDropDown.Value;
                
                % 调用CodeService运行Bug Finder
                % 基于规则第4调用CodeAnalysisService，根据TopModelCheckBox决定是否传递TreatAsTopMdl参数
                if app.TopModelCheckBox.Value
                    result = app.CodeService.runBugFinder(modelName, 'TreatAsTopMdl');
                else
                    result = app.CodeService.runBugFinder(modelName);
                end
                
                app.MessagesTextArea.Value = result.message;
                
                % 更新状态灯
                if result.success
                    app.BugFinderLamp.Color = 'green';
                else
                    app.BugFinderLamp.Color = 'red';
                end
                
                close(d);
                % figure(app.MainFigure) #tmpfix; % 规则第8确保主窗口在前台
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                app.MessagesTextArea.Value = sprintf('Bug Finder失败: %s', ME.message);
                app.BugFinderLamp.Color = 'red';
                uialert(app.MainFigure, sprintf('Bug Finder失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function RunCodeStandardsButtonPushed(app, varargin)
            % 运行Polyspace编码标准检查 - MISRA C等
            % 基于规则第4调用CodeAnalysisService
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保CodeService已初始化
                if isempty(app.CodeService)
                    app.CodeService = CodeAnalysisService.getInstance();
                end
                
                % 显示进度条（规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                    'Message', '正在运行编码标准检查...', 'Indeterminate', 'on');
                
                modelName = app.CurrentModelDropDown.Value;
                
                % 调用CodeService运行编码标准检查
                % 基于规则第4调用CodeAnalysisService，根据TopModelCheckBox决定是否传递TreatAsTopMdl参数
                if app.TopModelCheckBox.Value
                    result = app.CodeService.runCodeStandards(modelName, 'TreatAsTopMdl');
                else
                    result = app.CodeService.runCodeStandards(modelName);
                end
                
                app.MessagesTextArea.Value = result.message;
                
                % 更新状态灯
                if result.success
                    app.CodeStandardsLamp.Color = 'green';
                else
                    app.CodeStandardsLamp.Color = 'red';
                end
                
                close(d);
                % figure(app.MainFigure) #tmpfix; % 规则第8确保主窗口在前台
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                app.MessagesTextArea.Value = sprintf('编码标准检查失败: %s', ME.message);
                app.CodeStandardsLamp.Color = 'red';
                uialert(app.MainFigure, sprintf('编码标准检查失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function RunCodeProverButtonPushed(app, varargin)
            % 运行Polyspace Code Prover - 证明代码质量
            % 基于规则第4调用CodeAnalysisService
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 确保CodeService已初始化
                if isempty(app.CodeService)
                    app.CodeService = CodeAnalysisService.getInstance();
                end
                
                % 显示进度条（规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                    'Message', '正在运行Polyspace Code Prover...', 'Indeterminate', 'on');
                
                modelName = app.CurrentModelDropDown.Value;
                
                % 调用CodeService运行Code Prover
                % 基于规则第4调用CodeAnalysisService，根据TopModelCheckBox决定是否传递TreatAsTopMdl参数
                if app.TopModelCheckBox.Value
                    result = app.CodeService.runCodeProver(modelName, 'TreatAsTopMdl');
                else
                    result = app.CodeService.runCodeProver(modelName);
                end
                
                app.MessagesTextArea.Value = result.message;
                
                % 更新状态灯
                if result.success
                    app.CodeProverLamp.Color = 'green';
                else
                    app.CodeProverLamp.Color = 'red';
                end
                
                close(d);
                % figure(app.MainFigure) #tmpfix; % 规则第8确保主窗口在前台
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                app.MessagesTextArea.Value = sprintf('Code Prover失败: %s', ME.message);
                app.CodeProverLamp.Color = 'red';
                uialert(app.MainFigure, sprintf('Code Prover失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function RunAllCodeAnalysisButtonPushed(app, varargin)
            % 运行所有代码分析
            % 基于规则第4调用CodeAnalysisService统一管理
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                % 用户确认对话框（规则第8友好的用户交互）
                selection = uiconfirm(app.MainFigure, ...
                    ['将依次执行以下代码分析，这可能需要很长时间：' newline newline ...
                     '1. 代码生成' newline ...
                     '2. 代码检查 (Code Inspector)' newline ...
                     '3. 静态分析 (Polyspace)' newline ...
                     '4. 代码指标计算' newline newline ...
                     '是否继续执行？'], ...
                    '运行所有代码分析', ...
                    'Options', {'继续执行', '取消'}, ...
                    'DefaultOption', 2, ...
                    'CancelOption', 2, ...
                    'Icon', 'question');
                
                if strcmp(selection, '取消')
                    app.MessagesTextArea.Value = '用户取消了批量代码分析操作';
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 确保CodeService已初始化
                if isempty(app.CodeService)
                    app.CodeService = CodeAnalysisService.getInstance();
                end
                
                % 显示进度条（规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', '批量代码分析', ...
                    'Message', '正在执行所有代码分析...', 'Indeterminate', 'on');
                
                modelName = app.CurrentModelDropDown.Value;
                
                % 调用CodeService运行所有分析
                results = app.CodeService.runAllAnalysis(modelName);
                
                % 生成汇总消息
                successCount = sum(cellfun(@(r) r.success, results));
                totalCount = length(results);
                summaryMsg = sprintf('批量代码分析完成: %d/%d 成功\n\n', successCount, totalCount);
                
                for i = 1:length(results)
                    summaryMsg = sprintf('%s%d. %s\n', summaryMsg, i, results{i}.message);
                end
                
                app.MessagesTextArea.Value = summaryMsg;
                
                % 更新状态灯
                app.updateStatusLights();
                
                close(d);
                % figure(app.MainFigure) #tmpfix; % 规则第8确保主窗口在前台
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                app.MessagesTextArea.Value = sprintf('批量代码分析执行失败: %s', ME.message);
                uialert(app.MainFigure, sprintf('批量代码分析执行失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function DO178ObjectivesButtonPushed(app, varargin)
            app.MessagesTextArea.Value = 'DO-178C目标检查：验证所有安全目标的完成状态';
        end
        
        function ViewDocumentationButtonPushed(app, varargin)
            app.MessagesTextArea.Value = '查看文档：打开用户指南和API参考文档';
        end
        
        
        function loadDefaultSLREQXFile(app)
            % 加载默认的SLREQX文件到defaultReq标签页
            try
                % 获取工具脚本所在的目录作为基础路径 
                toolDir = fileparts(mfilename('fullpath'));
                % 项目根目录（假设工具在 tools/apps 子目录下）
                projectRoot = fullfile(toolDir, '..', '..');
                
                % 默认HLR文件路径
                hlrPath = fullfile(projectRoot, 'DO_02_Requirements', 'specification', 'T3', 'HLR.slreqx');
                hlrPath = PathUtils.normalizePath(hlrPath);
                
                % 获取defaultReq Tab的表格对象
                fieldName = matlab.lang.makeValidName('defaultReq');
                if isfield(app.DynamicReqTabs, fieldName)
                    defaultReqTable = app.DynamicReqTabs.(fieldName).table;
                    
                    % 加载HLR文件到defaultReq标签页
                    app.loadSLREQXToTable(hlrPath, defaultReqTable, 'defaultReq');
                end
                
            catch ME
                app.MessagesTextArea.Value = sprintf('加载默认需求文件时出错: %s', ME.message);
                % 基于第2条规则：错误必须明确报告，不设置空数据fallback
            end
        end
        
        function loadSLREQXToTable(app, filePath, targetTable, tableName)
            % 加载SLREQX文件到指定表格
            % 基于YML配置动态设置表格列
            if iscell(filePath)
                filePathChar = char(filePath{1});
            else
                filePathChar = char(filePath);
            end
            try
                if exist(filePathChar, 'file')
                    % 1使用MATLAB 2025a API同时兼容R2023b运行环境
                    app.MessagesTextArea.Value = sprintf('正在加载%s需求文件...', tableName);
                    drawnow;
                    
                    % 4通过Service Layer调用，而非直接调用底层工具函数
                    % loadFromSLREQX现在返回3个输出：success, tableData, columnConfig
                    [success, tableData, columnConfig] = app.ReqService.loadFromSLREQX(filePathChar);
                    
                    if success && ~isempty(tableData)
                        % 使用配置动态设置表格列
                        if ~isempty(fieldnames(columnConfig))
                            targetTable.ColumnName = columnConfig.columnNames;
                            targetTable.ColumnWidth = columnConfig.columnWidths;
                            fprintf('✅ 应用列配置：%d列\n', length(columnConfig.columnNames));
                        end
                        
                        % 直接使用返回的数据，无需标准化
                        targetTable.Data = tableData;
                        app.MessagesTextArea.Value = sprintf('%s需求文件加载成功: 共 %d 个需求，%d 列', ...
                            tableName, size(tableData, 1), size(tableData, 2));
                        app.updateDynamicRequirementTabData(tableName, tableData, targetTable, filePathChar);
                        
                        % 数据已经存储在targetTable.Data中，不再需要额外存储
                        % 动态Tab的数据管理更加简洁
                    elseif success
                        app.MessagesTextArea.Value = sprintf('%s需求文件加载成功但为空', tableName);
                        % 设置空数据
                        targetTable.Data = {};
                        app.updateDynamicRequirementTabData(tableName, {}, targetTable, filePathChar);
                    else
                        app.MessagesTextArea.Value = sprintf('%s需求文件加载失败', tableName);
                        % 设置空数据
                        targetTable.Data = {};
                        app.updateDynamicRequirementTabData(tableName, {}, targetTable, filePathChar);
                    end
                else
                    app.MessagesTextArea.Value = sprintf('%s需求文件不存在: %s', tableName, filePathChar);
                    % 设置空数据
                    targetTable.Data = {};
                    app.updateDynamicRequirementTabData(tableName, {}, targetTable, filePathChar);
                end
            catch ME
                app.MessagesTextArea.Value = sprintf('加载%s需求文件时出错: %s', tableName, ME.message);
                fprintf('错误详情: %s\n', ME.getReport());
                % 设置空数据
                targetTable.Data = {};
                app.updateDynamicRequirementTabData(tableName, {}, targetTable, filePathChar);
            end
        end
        
        function initializeRequirementsTree(app)
            % 初始化需求文件树
            try
                app.MessagesTextArea.Value = '正在构建需求文件树...';
                drawnow;
                
                % 获取项目根目录（从当前文件位置推断）
                projectRoot = fileparts(fileparts(fileparts(mfilename('fullpath'))));
                
                % 调用独立的需求文件树管理器构建树
                [success, errorMsg] = RequirementsTreeManager('buildTree', app.RequirementsTree, projectRoot);
                
                if success
                    app.MessagesTextArea.Value = '需求文件树构建完成';
                else
                    app.MessagesTextArea.Value = sprintf('构建需求文件树失败: %s', errorMsg);
                    fprintf('警告: %s\n', errorMsg);
                end
                
            catch ME
                app.MessagesTextArea.Value = sprintf('初始化需求文件树错误: %s', ME.message);
                fprintf('错误: 初始化需求文件树失败: %s\n', ME.message);
            end
        end
        % ==================== 动态Requirements Tab管理函数 ====================
        function createDynamicReqTab(app, tabName)
            % 基于第4条规则：保持GUI和业务逻辑分离
            % 创建一个新的动态需求Tab
            %
            % 输入:
            %   tabName - Tab名称（字符串）
            
            % 创建新Tab
            newTab = uitab(app.ReqStatusTabGroup);
            newTab.Title = tabName;
            
            % Tab布局：3行1列（加载按钮|过滤器|表格）
            tabGrid = uigridlayout(newTab, [3, 1]);
            tabGrid.RowHeight = {'fit', 'fit', '1x'};  % 加载按钮|过滤器|表格
            tabGrid.ColumnWidth = {'1x'};
            tabGrid.Padding = [5, 5, 5, 5];
            tabGrid.RowSpacing = 5;
            
            % 第1行：加载按钮
            loadButton = uibutton(tabGrid, 'push');
            loadButton.Layout.Row = 1;
            loadButton.Layout.Column = 1;
            loadButton.Text = '📂 加载选中的SLREQX文件';
            loadButton.BackgroundColor = [0.3 0.6 0.9];
            loadButton.FontColor = [1 1 1];
            loadButton.FontWeight = 'bold';
            loadButton.Tooltip = '从RequirementsTree中加载当前选中的SLREQX文件到此Tab';
            
            % 第3行：需求表格
            reqTable = uitable(tabGrid);
            reqTable.Layout.Row = 3;
            reqTable.Layout.Column = 1;
            reqTable.ColumnName = {'需求ID', '摘要', '状态', '优先级', '类型', '验证方法', '创建日期'};
            reqTable.ColumnEditable = false;
            reqTable.ColumnWidth = {80, 250, 80, 80, 100, 100, 100};

            % 第2行：过滤器面板（复用RequirementFilterManager）
            filterPanelStruct = app.createDynamicRequirementFilterPanel(tabGrid, 2, tabName, reqTable);
            
            % 设置加载按钮回调
            loadButton.ButtonPushedFcn = @(~,~) app.loadSelectedReqFileToTab(tabName, reqTable);
            
            % 保存Tab信息到动态结构体
            tabInfo = struct();
            tabInfo.tab = newTab;
            tabInfo.table = reqTable;
            tabInfo.filterPanel = filterPanelStruct.panel;
            tabInfo.filterControls = filterPanelStruct.controls;
            tabInfo.enabledFilters = filterPanelStruct.enabledFilters;
            tabInfo.loadButton = loadButton;
            tabInfo.grid = tabGrid;
            tabInfo.rawData = {};
            tabInfo.filteredData = {};
            tabInfo.lastLoadedFile = '';
            
            % 使用有效的字段名（替换非法字符）
            fieldName = matlab.lang.makeValidName(tabName);
            app.DynamicReqTabs.(fieldName) = tabInfo;
            
            % 切换到新创建的Tab
            app.ReqStatusTabGroup.SelectedTab = newTab;
        end
        
        function deleteDynamicReqTab(app, tabName)
            % 删除指定的动态Tab
            %
            % 输入:
            %   tabName - Tab名称（字符串）
            
            fieldName = matlab.lang.makeValidName(tabName);
            
            if ~isfield(app.DynamicReqTabs, fieldName)
                error('Tab "%s" 不存在', tabName);
            end
            
            % 获取Tab信息
            tabInfo = app.DynamicReqTabs.(fieldName);
            
            % 删除UI组件
            delete(tabInfo.tab);
            
            % 从结构体中移除
            app.DynamicReqTabs = rmfield(app.DynamicReqTabs, fieldName);
        end
        
        function exists = isReqTabNameExists(app, tabName)
            % 检查Tab名称是否已存在
            %
            % 输入:
            %   tabName - Tab名称（字符串）
            %
            % 输出:
            %   exists - 是否存在（布尔值）
            
            % 不再特殊处理HLR和SRS，所有Tab都是动态Tab
            % 检查是否在动态Tab中
            fieldName = matlab.lang.makeValidName(tabName);
            exists = isfield(app.DynamicReqTabs, fieldName);
        end
        
        function filterPanelStruct = createDynamicRequirementFilterPanel(app, parentGrid, row, reqType, targetTable)
            % 为动态Tab创建真实可用的过滤器面板
            % 4复用业务逻辑组件RequirementFilterManager

            filterPanelStruct = struct('panel', [], 'controls', struct(), 'enabledFilters', {{}});
            tabIdentifier = reqType;
            if isstring(tabIdentifier)
                tabIdentifier = char(tabIdentifier);
            end

            try
                % 3确保配置来源可追溯
                [filterConfig, attributeConfig] = RequirementFilterManager.loadFilterConfig();
                [enabledFilters, filterCount] = RequirementFilterManager.parseEnabledFilters(filterConfig);

                if filterCount == 0
                    placeholderPanel = uipanel(parentGrid);
                    placeholderPanel.Layout.Row = row;
                    placeholderPanel.Layout.Column = 1;
                    placeholderPanel.Title = sprintf('%s Filters', reqType);
                    placeholderPanel.FontWeight = 'bold';
                    placeholderPanel.BackgroundColor = [0.95 0.98 1];

                    placeholderGrid = uigridlayout(placeholderPanel, [1, 1]);
                    placeholderGrid.Padding = [10, 10, 10, 10];

                    placeholder = uilabel(placeholderGrid);
                    placeholder.Layout.Row = 1;
                    placeholder.Layout.Column = 1;
                    placeholder.Text = '无可用过滤器配置';
                    placeholder.HorizontalAlignment = 'center';
                    placeholder.FontAngle = 'italic';

                    filterPanelStruct.panel = placeholderPanel;
                    return;
                end

                [filterPanel, filterControls] = RequirementFilterManager.createFilterPanel(parentGrid, row, reqType, filterConfig, attributeConfig);

                % 绑定过滤器回调
                controlNames = fieldnames(filterControls);
                for i = 1:length(controlNames)
                    controlName = controlNames{i};
                    if strcmp(controlName, 'resetButton')
                        continue;
                    end

                    controlHandle = filterControls.(controlName);

                    if isa(controlHandle, 'matlab.ui.control.DropDown') || isa(controlHandle, 'matlab.ui.control.EditField')
                        controlHandle.ValueChangedFcn = @(src, event) app.dynamicFilterValueChanged(tabIdentifier, enabledFilters, filterControls, targetTable);
                    end
                end

                if isfield(filterControls, 'resetButton') && isvalid(filterControls.resetButton)
                    filterControls.resetButton.ButtonPushedFcn = @(src, event) app.resetDynamicRequirementFilters(tabIdentifier, enabledFilters, filterControls, targetTable);
                end

                filterPanelStruct.panel = filterPanel;
                filterPanelStruct.controls = filterControls;
                filterPanelStruct.enabledFilters = enabledFilters;

            catch ME
                warningPanel = uipanel(parentGrid);
                warningPanel.Layout.Row = row;
                warningPanel.Layout.Column = 1;
                warningPanel.Title = sprintf('%s Filters', reqType);
                warningPanel.FontWeight = 'bold';
                warningPanel.BackgroundColor = [1 0.94 0.94];

                warningGrid = uigridlayout(warningPanel, [1, 1]);
                warningGrid.Padding = [10, 10, 10, 10];

                warningLabel = uilabel(warningGrid);
                warningLabel.Layout.Row = 1;
                warningLabel.Layout.Column = 1;
                warningLabel.Text = sprintf('过滤配置加载失败: %s', ME.message);
                warningLabel.HorizontalAlignment = 'center';
                warningLabel.FontColor = [0.8 0 0];
                warningLabel.FontWeight = 'bold';

                filterPanelStruct.panel = warningPanel;
                fprintf('❌ 动态过滤器初始化失败(%s): %s\n', tabIdentifier, ME.getReport());
            end
        end

        function dynamicFilterValueChanged(app, tabIdentifier, enabledFilters, filterControls, targetTable)
            % 动态Tab过滤控件值变化回调
            % 4复用业务逻辑层过滤函数
            try
                fieldName = matlab.lang.makeValidName(tabIdentifier);

                if ~isfield(app.DynamicReqTabs, fieldName)
                    error('DynamicRequirementTab:notFound', '动态需求Tab "%s" 未注册', tabIdentifier);
                end

                tabInfo = app.DynamicReqTabs.(fieldName);

                if nargin < 4 || isempty(targetTable) || ~isvalid(targetTable)
                    targetTable = tabInfo.table;
                    if isempty(targetTable) || ~isvalid(targetTable)
                        return;
                    end
                end

                if isempty(tabInfo.rawData)
                    targetTable.Data = {};
                    tabInfo.filteredData = {};
                    app.DynamicReqTabs.(fieldName) = tabInfo;
                    return;
                end

                if isempty(enabledFilters) || isempty(fieldnames(filterControls))
                    filteredData = tabInfo.rawData;
                else
                    filters = RequirementFilterManager.buildFilterStruct(filterControls, enabledFilters);
                    filteredData = RequirementsDataTransformer.filterRequirements(tabInfo.rawData, filters);
                end

                targetTable.Data = filteredData;
                tabInfo.filteredData = filteredData;
                app.DynamicReqTabs.(fieldName) = tabInfo;

                app.MessagesTextArea.Value = sprintf('Tab %s 过滤完成: %d/%d 条', tabIdentifier, size(filteredData, 1), size(tabInfo.rawData, 1));

            catch ME
                errMsg = sprintf('动态过滤器应用错误(%s): %s', tabIdentifier, ME.message);
                app.MessagesTextArea.Value = errMsg;
                fprintf('❌ %s\n', ME.getReport());
            end
        end

        function resetDynamicRequirementFilters(app, tabIdentifier, enabledFilters, filterControls, targetTable)
            % 动态Tab过滤控件重置回调
            % 2重置时明确反馈状态
            try
                if ~isempty(fieldnames(filterControls))
                    RequirementFilterManager.resetFilters(filterControls, enabledFilters);
                end

                app.dynamicFilterValueChanged(tabIdentifier, enabledFilters, filterControls, targetTable);
                app.MessagesTextArea.Value = sprintf('Tab %s 过滤器已重置', tabIdentifier);

            catch ME
                errMsg = sprintf('动态过滤器重置错误(%s): %s', tabIdentifier, ME.message);
                app.MessagesTextArea.Value = errMsg;
                fprintf('❌ %s\n', ME.getReport());
            end
        end

        function updateDynamicRequirementTabData(app, tabName, tableData, targetTable, sourcePath)
            % 更新动态需求Tab的数据缓存和过滤结果
            % 3保持数据可追溯
            try
                fieldName = matlab.lang.makeValidName(tabName);
                if ~isfield(app.DynamicReqTabs, fieldName)
                    return;
                end

                tabInfo = app.DynamicReqTabs.(fieldName);

                if nargin >= 4 && ~isempty(targetTable) && isvalid(targetTable)
                    tabInfo.table = targetTable;
                end

                if nargin >= 5 && ~isempty(sourcePath)
                    tabInfo.lastLoadedFile = char(sourcePath);
                end

                tabInfo.rawData = tableData;
                tabInfo.filteredData = tableData;

                app.DynamicReqTabs.(fieldName) = tabInfo;

                hasFilters = isfield(tabInfo, 'filterControls') && ...
                    isstruct(tabInfo.filterControls) && ~isempty(fieldnames(tabInfo.filterControls));

                if hasFilters && ~isempty(tabInfo.enabledFilters)
                app.dynamicFilterValueChanged(tabName, tabInfo.enabledFilters, tabInfo.filterControls, tabInfo.table);
                else
                    if isfield(tabInfo, 'table') && isvalid(tabInfo.table)
                        tabInfo.table.Data = tableData;
                    end
                    tabInfo.filteredData = tableData;
                    app.DynamicReqTabs.(fieldName) = tabInfo;
                end

            catch ME
                fprintf('❌ 动态Tab数据更新失败(%s): %s\n', tabName, ME.getReport());
            end
        end
        
        function loadSelectedReqFileToTab(app, tabName, targetTable)
            % 基于第8条规则：超过1秒的操作要有进度条
            % 从RequirementsTree加载选中的SLREQX文件到指定Tab的表格
            %
            % 输入:
            %   tabName - Tab名称
            %   targetTable - 目标表格对象
            
            try
                % 获取选中的节点
                selectedNodes = app.RequirementsTree.SelectedNodes;
                
                if isempty(selectedNodes)
                    uialert(app.MainFigure, '请先在需求文件树中选择一个SLREQX文件', '提示');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 获取文件路径（UserData中存储）
                selectedNode = selectedNodes(1);
                
                if ~isprop(selectedNode, 'UserData') || isempty(selectedNode.UserData)
                    uialert(app.MainFigure, '所选节点不是有效的需求文件', '错误');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                fileInfo = selectedNode.UserData;
                % fileInfo应为struct，包含Type和Path字段（依据实际数据结构）
                if isstruct(fileInfo) && isfield(fileInfo, 'Path')
                    filePath = fileInfo.Path;
                else
                    filePath = fileInfo; % fallback，仅当老版本或单字符串情况
                end

                % 检查文件扩展名
                [~, ~, ext] = fileparts(filePath);
                if ~strcmpi(ext, '.slreqx')
                    uialert(app.MainFigure, '请选择SLREQX文件', '错误');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 显示进度对话框
                progressDlg = uiprogressdlg(app.MainFigure, 'Title', '加载需求文件', ...
                    'Message', sprintf('正在加载%s...', selectedNode.Text), ...
                    'Indeterminate', 'on');
                
                % 调用loadSLREQXToTable函数
                app.loadSLREQXToTable(filePath, targetTable, tabName);
                
                close(progressDlg);
                
                app.MessagesTextArea.Value = sprintf('成功加载需求文件到Tab "%s": %s', tabName, selectedNode.Text);
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('progressDlg', 'var') && isvalid(progressDlg)
                    close(progressDlg);
                end
                uialert(app.MainFigure, sprintf('加载需求文件失败: %s', ME.message), '错误');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        % ==================== End of 动态Requirements Tab管理函数 ====================
        
        function filterPanel = createALMFilterPanel(app, parentGrid, row, almType)
            % 创建ALM（PR/CR）过滤器面板
            filterPanel = uipanel(parentGrid);
            filterPanel.Layout.Row = row;
            filterPanel.Layout.Column = 1;
            filterPanel.Title = sprintf('%s过滤器', almType);
            filterPanel.FontWeight = 'bold';
            filterPanel.BackgroundColor = [0.95 0.95 1];
            
            % 过滤器网格布局：2行5列（标签行|控件行）
            filterGrid = uigridlayout(filterPanel, [2, 5]);
            filterGrid.RowHeight = {'fit', 'fit'};  % 标签行和控件行都自适应
            filterGrid.ColumnWidth = {120, 120, 180, 80, 80};  % 状态|优先级|搜索|重置|空白
            filterGrid.Padding = [8, 8, 8, 8];
            filterGrid.ColumnSpacing = 8;
            filterGrid.RowSpacing = 3;
            
            % 第1行：标签
            statusLabel = uilabel(filterGrid);
            statusLabel.Layout.Row = 1;
            statusLabel.Layout.Column = 1;
            statusLabel.Text = '状态';
            statusLabel.FontWeight = 'bold';
            statusLabel.HorizontalAlignment = 'center';
            statusLabel.FontSize = 10;
            
            priorityLabel = uilabel(filterGrid);
            priorityLabel.Layout.Row = 1;
            priorityLabel.Layout.Column = 2;
            if strcmp(almType, 'PR')
                priorityLabel.Text = '严重性';
            else
                priorityLabel.Text = '优先级';
            end
            priorityLabel.FontWeight = 'bold';
            priorityLabel.HorizontalAlignment = 'center';
            priorityLabel.FontSize = 10;
            
            searchLabel = uilabel(filterGrid);
            searchLabel.Layout.Row = 1;
            searchLabel.Layout.Column = 3;
            searchLabel.Text = '搜索';
            searchLabel.FontWeight = 'bold';
            searchLabel.HorizontalAlignment = 'center';
            searchLabel.FontSize = 10;
            
            resetLabel = uilabel(filterGrid);
            resetLabel.Layout.Row = 1;
            resetLabel.Layout.Column = 4;
            resetLabel.Text = '重置';
            resetLabel.FontWeight = 'bold';
            resetLabel.HorizontalAlignment = 'center';
            resetLabel.FontSize = 10;
            
            % 第2行：控件
            if strcmp(almType, 'PR')
                % PR过滤器控件
                app.PRStatusFilter = uidropdown(filterGrid);
                app.PRStatusFilter.Layout.Row = 2;
                app.PRStatusFilter.Layout.Column = 1;
                app.PRStatusFilter.Items = {'All'};  % 初始只有All，后续动态更新
                app.PRStatusFilter.Value = 'All';
                app.PRStatusFilter.ValueChangedFcn = @(dd,event) app.PRFilterChanged();
                
                app.PRSeverityFilter = uidropdown(filterGrid);
                app.PRSeverityFilter.Layout.Row = 2;
                app.PRSeverityFilter.Layout.Column = 2;
                app.PRSeverityFilter.Items = {'All'};  % 初始只有All，后续动态更新
                app.PRSeverityFilter.Value = 'All';
                app.PRSeverityFilter.ValueChangedFcn = @(dd,event) app.PRFilterChanged();
                
                app.PRSearchField = uieditfield(filterGrid, 'text');
                app.PRSearchField.Layout.Row = 2;
                app.PRSearchField.Layout.Column = 3;
                app.PRSearchField.Placeholder = '搜索PR编号或标题...';
                app.PRSearchField.ValueChangedFcn = @(ef,event) app.PRFilterChanged();
                
                app.PRResetFilterButton = uibutton(filterGrid, 'push');
                app.PRResetFilterButton.Layout.Row = 2;
                app.PRResetFilterButton.Layout.Column = 4;
                app.PRResetFilterButton.Text = '重置';
                app.PRResetFilterButton.BackgroundColor = [0.8 0.8 0.8];
                app.PRResetFilterButton.ButtonPushedFcn = @(btn,event) app.resetPRFilter();
                
            else
                % CR过滤器控件
                app.CRStatusFilter = uidropdown(filterGrid);
                app.CRStatusFilter.Layout.Row = 2;
                app.CRStatusFilter.Layout.Column = 1;
                app.CRStatusFilter.Items = {'All'};  % 初始只有All，后续动态更新
                app.CRStatusFilter.Value = 'All';
                app.CRStatusFilter.ValueChangedFcn = @(dd,event) app.CRFilterChanged();
                
                app.CRPriorityFilter = uidropdown(filterGrid);
                app.CRPriorityFilter.Layout.Row = 2;
                app.CRPriorityFilter.Layout.Column = 2;
                app.CRPriorityFilter.Items = {'All'};  % 初始只有All，后续动态更新
                app.CRPriorityFilter.Value = 'All';
                app.CRPriorityFilter.ValueChangedFcn = @(dd,event) app.CRFilterChanged();
                
                app.CRSearchField = uieditfield(filterGrid, 'text');
                app.CRSearchField.Layout.Row = 2;
                app.CRSearchField.Layout.Column = 3;
                app.CRSearchField.Placeholder = '搜索CR编号或标题...';
                app.CRSearchField.ValueChangedFcn = @(ef,event) app.CRFilterChanged();
                
                app.CRResetFilterButton = uibutton(filterGrid, 'push');
                app.CRResetFilterButton.Layout.Row = 2;
                app.CRResetFilterButton.Layout.Column = 4;
                app.CRResetFilterButton.Text = '重置';
                app.CRResetFilterButton.BackgroundColor = [0.8 0.8 0.8];
                app.CRResetFilterButton.ButtonPushedFcn = @(btn,event) app.resetCRFilter();
            end
        end
        
        function updateALMFilterOptions(app, almType, tableData)
            % 从实际数据中动态更新ALM过滤器的下拉选项
            % 基于规则第3条：UI自适应后台server的数据
            %
            % 输入：
            %   almType - 类型（'PR' 或 'CR'）
            %   tableData - 表格数据（cell array，6列）
            %
            % 功能：提取第4列（状态）和第5列（优先级/严重性）的唯一值，
            %       按字母顺序排序后更新下拉框选项
            
            try
                if isempty(tableData) || size(tableData, 2) < 6
                    % 数据为空或格式不正确，只显示"All"
                    if strcmp(almType, 'PR')
                        app.PRStatusFilter.Items = {'All'};
                        app.PRStatusFilter.Value = 'All';
                        app.PRSeverityFilter.Items = {'All'};
                        app.PRSeverityFilter.Value = 'All';
                    else
                        app.CRStatusFilter.Items = {'All'};
                        app.CRStatusFilter.Value = 'All';
                        app.CRPriorityFilter.Items = {'All'};
                        app.CRPriorityFilter.Value = 'All';
                    end
                    return;
                end
                
                % 提取第4列（状态）的唯一值
                statusColumn = tableData(:, 4);
                uniqueStatuses = unique(statusColumn(~cellfun(@isempty, statusColumn)));
                % 按字母顺序排序
                uniqueStatuses = sort(uniqueStatuses);
                % 添加"All"在最前面
                statusOptions = ['All'; uniqueStatuses];
                
                % 提取第5列（优先级/严重性）的唯一值
                priorityColumn = tableData(:, 5);
                uniquePriorities = unique(priorityColumn(~cellfun(@isempty, priorityColumn)));
                % 按字母顺序排序
                uniquePriorities = sort(uniquePriorities);
                % 添加"All"在最前面
                priorityOptions = ['All'; uniquePriorities];
                
                % 更新下拉框选项（临时禁用回调，避免触发过滤）
                if strcmp(almType, 'PR')
                    % 保存当前选择和回调函数
                    currentStatusValue = app.PRStatusFilter.Value;
                    currentSeverityValue = app.PRSeverityFilter.Value;
                    statusCallback = app.PRStatusFilter.ValueChangedFcn;
                    severityCallback = app.PRSeverityFilter.ValueChangedFcn;
                    
                    % 临时禁用回调
                    app.PRStatusFilter.ValueChangedFcn = [];
                    app.PRSeverityFilter.ValueChangedFcn = [];
                    
                    % 更新选项
                    app.PRStatusFilter.Items = statusOptions';
                    app.PRSeverityFilter.Items = priorityOptions';
                    
                    % 恢复选择（如果旧值还在新选项中）
                    if ismember(currentStatusValue, statusOptions)
                        app.PRStatusFilter.Value = currentStatusValue;
                    else
                        app.PRStatusFilter.Value = 'All';
                    end
                    
                    if ismember(currentSeverityValue, priorityOptions)
                        app.PRSeverityFilter.Value = currentSeverityValue;
                    else
                        app.PRSeverityFilter.Value = 'All';
                    end
                    
                    % 恢复回调
                    app.PRStatusFilter.ValueChangedFcn = statusCallback;
                    app.PRSeverityFilter.ValueChangedFcn = severityCallback;
                    
                    fprintf('✅ PR过滤器选项已更新: 状态%d项, 严重性%d项\n', ...
                        length(statusOptions), length(priorityOptions));
                else
                    % 保存当前选择和回调函数
                    currentStatusValue = app.CRStatusFilter.Value;
                    currentPriorityValue = app.CRPriorityFilter.Value;
                    statusCallback = app.CRStatusFilter.ValueChangedFcn;
                    priorityCallback = app.CRPriorityFilter.ValueChangedFcn;
                    
                    % 临时禁用回调
                    app.CRStatusFilter.ValueChangedFcn = [];
                    app.CRPriorityFilter.ValueChangedFcn = [];
                    
                    % 更新选项
                    app.CRStatusFilter.Items = statusOptions';
                    app.CRPriorityFilter.Items = priorityOptions';
                    
                    % 恢复选择（如果旧值还在新选项中）
                    if ismember(currentStatusValue, statusOptions)
                        app.CRStatusFilter.Value = currentStatusValue;
                    else
                        app.CRStatusFilter.Value = 'All';
                    end
                    
                    if ismember(currentPriorityValue, priorityOptions)
                        app.CRPriorityFilter.Value = currentPriorityValue;
                    else
                        app.CRPriorityFilter.Value = 'All';
                    end
                    
                    % 恢复回调
                    app.CRStatusFilter.ValueChangedFcn = statusCallback;
                    app.CRPriorityFilter.ValueChangedFcn = priorityCallback;
                    
                    fprintf('✅ CR过滤器选项已更新: 状态%d项, 优先级%d项\n', ...
                        length(statusOptions), length(priorityOptions));
                end
                
            catch ME
                fprintf('❌ 更新%s过滤器选项失败: %s\n', almType, ME.message);
            end
        end
        
        
        function setupResizableLayout(app)
            % 设置可调整宽度的布局系统
            % 存储当前布局状态
            app.CurrentLayoutIndex = 1;  % 当前布局索引
            app.LayoutPresets = {
                {'1x', 5, '4x'};      % 默认1:4比例 (20% | 80%)
                {'1.5x', 5, '3x'};    % 稍微增加左侧 (33% | 67%)
                {'2x', 5, '2x'};      % 平等分布 (50% | 50%)  
                {'3x', 5, '1.5x'};    % 左侧强调 (67% | 33%)
                {'4x', 5, '1x'}       % 左侧最大化 (80% | 20%)
            };
            
            % 为分割线添加点击回调
            app.Divider1.ButtonDownFcn = @(src, event) app.adjustLayout(1);
        end
        
        function adjustLayout(app, dividerNum)
            % 调整布局比例
            try
                % 切换到下一个布局预设
                app.CurrentLayoutIndex = app.CurrentLayoutIndex + 1;
                if app.CurrentLayoutIndex > length(app.LayoutPresets)
                    app.CurrentLayoutIndex = 1;
                end
                
                % 应用新的布局
                newLayout = app.LayoutPresets{app.CurrentLayoutIndex};
                app.ResizableGrid.ColumnWidth = newLayout;
                
                % 更新用户反馈
                layoutNames = {
                    '默认布局 (20% | 80%) - Status区占主要空间',
                    '稍微增加Coverage (33% | 67%)', 
                    '平等分布 (50% | 50%)',
                    'Coverage区强调 (67% | 33%)',
                    'Coverage区最大化 (80% | 20%)'
                };
                
                app.MessagesTextArea.Value = sprintf('Requirements布局已调整: %s', layoutNames{app.CurrentLayoutIndex});
                
                % 视觉反馈 - 临时改变分割线颜色
                originalColor = app.Divider1.BackgroundColor;
                app.Divider1.BackgroundColor = [0.2, 0.8, 0.2];  % 绿色
                
                % 0.5秒后恢复原色
                t = timer('StartDelay', 0.5, 'TimerFcn', @(~,~) set(app.Divider1, 'BackgroundColor', originalColor));
                start(t);
                
            catch ME
                app.MessagesTextArea.Value = sprintf('布局调整失败: %s', ME.message);
            end
        end
        
        % === 辅助函数 ===
        function isValid = validateCurrentModel(app)
            % 验证当前模型是否已选择且有效 - 基于规则第4使用ModelManagementService
            % 与mbdWorkflowTool_m保持一致
            isValid = true;
            
            try
                % 确保ModelService已初始化（单例模式）
                if isempty(app.ModelService)
                    app.ModelService = ModelManagementService.getInstance();
                end
                
                % 使用ModelManagementService验证模型
                modelList = app.ModelService.getModelList();
                if ~ismember(app.CurrentModelDropDown.Value, modelList)
                    uialert(app.MainFigure, 'Please select model first.', 'MBD Platform', ...
                        'Icon', 'warning');
                    % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台
                    isValid = false;
                end
            catch ME
                % Service调用失败，使用简单验证
                fprintf('⚠️ ModelService验证失败: %s\n', ME.message);
                if isempty(app.CurrentModelDropDown.Value) || ...
                   strcmp(app.CurrentModelDropDown.Value, '') || ...
                   strcmp(app.CurrentModelDropDown.Value, '无模型')
                    uialert(app.MainFigure, 'Please select model first.', 'MBD Platform', ...
                        'Icon', 'warning');
                    % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台
                    isValid = false;
                end
            end
        end

        
        function refreshModelList(app, showProgress)
            % 刷新模型列表并更新下拉菜单
            % 基于规则第4调用ModelManagementService
            % 输入参数：showProgress - 是否显示进度条（默认true，基于规则第8条）
            if nargin < 2 || isempty(showProgress)
                showProgress = true;
            end
            
            % 显示进度条（规则第8条）
            dlg = [];
            if showProgress
                try
                    dlg = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                        'Message', '正在刷新模型列表...', 'Indeterminate', 'on');
                catch
                    % 如果主窗口未初始化，不显示进度条
                    dlg = [];
                end
            end
            
            try
                % 确保ModelService已初始化（单例模式）
                if isempty(app.ModelService)
                    app.ModelService = ModelManagementService.getInstance();
                end
                
                % 调用ModelService获取模型列表
                modelList = app.ModelService.getModelList(true); % 强制刷新
                
                if ~isempty(modelList)
                    app.CurrentModelDropDown.Items = modelList;
                    if isempty(app.CurrentModelDropDown.Value) || ...
                       ~ismember(app.CurrentModelDropDown.Value, modelList)
                        app.CurrentModelDropDown.Value = modelList{1}; % 设置默认选择第一个模型
                    end
                    app.MessagesTextArea.Value = sprintf('已检测到 %d 个模型：%s', length(modelList), strjoin(modelList, ', '));
                else
                    app.CurrentModelDropDown.Items = {'无模型'};
                    app.CurrentModelDropDown.Value = '无模型';
                    app.MessagesTextArea.Value = '未在项目中检测到任何有效的Simulink模型';
                end
                
                % 更新状态指示灯
                app.updateStatusLights();
                
                % 关闭进度条
                if ~isempty(dlg) && isvalid(dlg)
                    close(dlg);
                end
                
            catch ME
                % 关闭进度条（如果存在）
                if exist('dlg', 'var') && ~isempty(dlg) && isvalid(dlg)
                    close(dlg);
                end
                
                app.CurrentModelDropDown.Items = {'无模型'};
                app.CurrentModelDropDown.Value = '无模型';
                app.MessagesTextArea.Value = sprintf('模型检测失败: %s', ME.message);
                fprintf('❌ refreshModelList失败: %s\n', ME.message);
            end
        end
        
        function refreshModelTree(app, showProgress)
            % 刷新模型树
            % 基于规则第4调用ModelManagementService
            % 输入参数：showProgress - 是否显示进度条（默认true，基于规则第8条）
            if nargin < 2 || isempty(showProgress)
                showProgress = true;
            end
            
            % 显示进度条（规则第8条）
            dlg = [];
            if showProgress
                try
                    dlg = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                        'Message', '正在刷新模型树...', 'Indeterminate', 'on');
                catch
                    % 如果主窗口未初始化，不显示进度条
                    dlg = [];
                end
            end
            
            try
                % 确保ModelService已初始化（单例模式）
                if isempty(app.ModelService)
                    app.ModelService = ModelManagementService.getInstance();
                end
                
                % 调用ModelService刷新模型树
                app.ModelService.refreshModelTree(app.ModelTree);
                
                % 刷新完成后，重新高亮当前模型
                app.highlightCurrentModelInTree();
                
                % 关闭进度条
                if ~isempty(dlg) && isvalid(dlg)
                    close(dlg);
                end
                
            catch ME
                % 关闭进度条（如果存在）
                if exist('dlg', 'var') && ~isempty(dlg) && isvalid(dlg)
                    close(dlg);
                end
                
                fprintf('❌ refreshModelTree失败: %s\n', ME.message);
                app.MessagesTextArea.Value = sprintf('模型树刷新失败: %s', ME.message);
            end
        end
        
        function highlightCurrentModelInTree(app)
            % 在模型树中高亮当前选中的模型
            try
                % 获取当前模型名称
                currentModel = app.CurrentModelDropDown.Value;
                
                % 如果当前模型无效，清除所有高亮
                if isempty(currentModel) || strcmp(currentModel, '无模型') || strcmp(currentModel, '正在检测模型...')
                    app.clearAllHighlights();
                    return;
                end
                
                % 首先清除所有现有的高亮
                app.clearAllHighlights();
                
                % 遍历树节点，找到匹配的模型并高亮
                app.highlightNodeRecursive(app.ModelTree.Children, currentModel);
                
            catch ME
                fprintf('高亮当前模型时发生错误: %s\n', ME.message);
            end
        end
        
        function clearAllHighlights(app)
            % 清除模型树中所有节点的高亮
            try
                if ~isempty(app.ModelTree.Children)
                    app.clearHighlightRecursive(app.ModelTree.Children);
                end
            catch ME
                fprintf('清除高亮时发生错误: %s\n', ME.message);
            end
        end
        
        function clearHighlightRecursive(app, nodes)
            % 递归清除节点高亮
            for i = 1:length(nodes)
                node = nodes(i);
                % 移除高亮格式，保留原始模型名称
                originalText = node.Text;
                if startsWith(originalText, '★★★【') && endsWith(originalText, '】★★★')
                    % 提取原始模型名称
                    originalName = extractBetween(originalText, '★★★【', '】★★★');
                    if ~isempty(originalName)
                        node.Text = originalName{1};
                    end
                end
                
                % 递归处理子节点
                if ~isempty(node.Children)
                    app.clearHighlightRecursive(node.Children);
                end
            end
        end
        
        function found = highlightNodeRecursive(app, nodes, targetModel)
            % 递归查找并高亮目标模型节点
            found = false;
            
            for i = 1:length(nodes)
                node = nodes(i);
                nodeText = node.Text;
                
                % 检查当前节点是否匹配目标模型
                if strcmp(nodeText, targetModel)
                    % 应用高亮格式：使用特殊符号来突出显示，保持原始名称
                    highlightedText = ['★★★【' targetModel '】★★★'];
                    node.Text = highlightedText;
                    found = true;
                    return;
                end
                
                % 递归检查子节点
                if ~isempty(node.Children)
                    childFound = app.highlightNodeRecursive(node.Children, targetModel);
                    if childFound
                        found = true;
                        return;
                    end
                end
            end
        end
        
        
        function modelName = getCurrentSelectedModel(app)
            % 获取当前选中的模型名称
            modelName = '';
            
            % 首先尝试从树中获取选中的模型
            if ~isempty(app.ModelTree.SelectedNodes)
                selectedNode = app.ModelTree.SelectedNodes(1);
                if ~strcmp(selectedNode.Text, 'Design Models')
                    modelName = selectedNode.Text;
                    return;
                end
            end
            
            % 如果树中没有选中有效模型，使用下拉框中的值
            currentModel = app.CurrentModelDropDown.Value;
            if ~isempty(currentModel) && ~strcmp(currentModel, '无模型')
                modelName = currentModel;
            end
        end
        
        function modelPath = findModelPath(app, modelName)
            % 查找模型文件的完整路径 - 4条（架构分离原则）
            % 使用ProjectPathManager获取标准化的模型路径
            %
            % 输入参数:
            %   modelName - 模型名称
            % 输出参数:
            %   modelPath - 模型文件的完整路径
            %
            % 实现说明：
            %   - 优先使用ProjectPathManager获取标准路径
            %   - 回退到MATLAB路径查找
            %   - 符合项目结构规范
            
            modelPath = '';
            
            try
                % 方式1：使用ProjectPathManager获取标准模型路径（推荐）
                % 基于规则第4使用Service层API
                modelDir = ProjectPathManager.getDirPath('model', modelName);
                
                % 检查.slx文件
                slxPath = fullfile(modelDir, [modelName, '.slx']);
                if exist(slxPath, 'file')
                    modelPath = slxPath;
                    return;
                end
                
                % 检查.mdl文件
                mdlPath = fullfile(modelDir, [modelName, '.mdl']);
                if exist(mdlPath, 'file')
                    modelPath = mdlPath;
                    return;
                end
                
            catch ME
                % ProjectPathManager查找失败，尝试回退方式
                warning('findModelPath:ProjectPathManagerFailed', ...
                    '使用ProjectPathManager查找模型失败: %s', ME.message);
            end
            
            % 方式2：回退到MATLAB路径查找
            % 检查模型是否在MATLAB路径中
            if exist([modelName '.slx'], 'file')
                modelPath = which([modelName '.slx']);
                return;
            end
            
            if exist([modelName '.mdl'], 'file')
                modelPath = which([modelName '.mdl']);
                return;
            end
            
            % 如果都找不到，返回空字符串
            warning('findModelPath:ModelNotFound', ...
                '未找到模型文件: %s', modelName);
        end
        function startLogging(app)
            % 开始日志记录 - 4条（架构分离原则）
            % 使用LoggingService统一管理日志
            % 
            % 功能：
            %   - 启用日志记录
            %   - DetailedLoggingCheckBox已移至Options->Manual菜单
            %   - 默认启用日志
            %
            % 注意：DetailedLoggingCheckBox已从主界面移至Options->Manual菜单
            
            try
                % 获取LoggingService单例
                logger = LoggingService.getInstance();
                
                % 默认启用日志
                    logger.setEnableLogging(true);
                fprintf('✅ 详细日志已启用（默认模式）\n');
            catch ME
                % 如果LoggingService不可用，静默忽略
                % 不影响主要功能
                warning('LoggingService:Unavailable', '%s', ME.message);
            end
        end
        
        function DetailedLoggingCheckBoxValueChanged(app, checkbox)
            % 详细日志复选框值变化回调 - 4条（架构分离原则）
            % 当复选框值改变时，立即更新LoggingService
            % 参数:
            %   app - 应用实例
            %   checkbox - 复选框对象
            
            value = checkbox.Value;
            
            try
                logger = LoggingService.getInstance();
                logger.setEnableLogging(value);
                
                % 在消息区域显示状态变化
                if value
                    msgObj = MessageService.create('✅ 详细日志已启用', ...
                        'Level', MessageService.LEVEL_SUCCESS, ...
                        'Timestamp', true);
                else
                    msgObj = MessageService.create('ℹ️  详细日志已禁用', ...
                        'Level', MessageService.LEVEL_INFO, ...
                        'Timestamp', true);
                end
                
                app.MessagesTextArea.Value = msgObj.text;
                app.MessagesTextArea.FontColor = msgObj.color;
                
                % 确保主窗口回到前台（规则第8条）
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 显示错误消息
                msgObj = MessageService.create(sprintf('⚠️  无法更新日志设置: %s', ME.message), ...
                    'Level', MessageService.LEVEL_WARNING);
                app.MessagesTextArea.Value = msgObj.text;
                app.MessagesTextArea.FontColor = msgObj.color;
            end
        end
        
        function ManualMenuSelected(app, ~, ~)
            % Manual菜单选中回调
            % 4条（架构分离原则）和第8条（GUI友好界面）
            % 创建一个对话框，包含详细日志设置选项
            
            % 创建对话框
            dlg = uifigure('Name', 'Manual Settings', ...
                'Position', [100, 100, 400, 200], ...
                'Resize', 'off');
            
            % 创建网格布局
            grid = uigridlayout(dlg, [3, 1]);
            grid.RowHeight = {'fit', 'fit', '1x'};
            grid.Padding = [20, 20, 20, 20];
            grid.RowSpacing = 15;
            
            % 标题标签
            titleLabel = uilabel(grid);
            titleLabel.Layout.Row = 1;
            titleLabel.Layout.Column = 1;
            titleLabel.Text = '手动设置 (Manual Settings)';
            titleLabel.FontSize = 14;
            titleLabel.FontWeight = 'bold';
            
            % 详细日志复选框
            detailedLoggingCheckBox = uicheckbox(grid);
            detailedLoggingCheckBox.Layout.Row = 2;
            detailedLoggingCheckBox.Layout.Column = 1;
            detailedLoggingCheckBox.Text = '启用详细日志 (Detailed Logging)';
            detailedLoggingCheckBox.Tooltip = '启用后将记录详细的操作日志到 work/logs 目录';
            
            % 获取当前日志状态
            try
                logger = LoggingService.getInstance();
                detailedLoggingCheckBox.Value = logger.EnableLogging;
            catch
                detailedLoggingCheckBox.Value = true;  % 默认启用
            end
            
            % 绑定回调函数
            detailedLoggingCheckBox.ValueChangedFcn = @(src, ~) app.DetailedLoggingCheckBoxValueChanged(src);
            
            % 添加关闭按钮
            closeButton = uibutton(grid, 'push');
            closeButton.Layout.Row = 3;
            closeButton.Layout.Column = 1;
            closeButton.Text = '关闭';
            closeButton.ButtonPushedFcn = @(~,~) close(dlg);
            closeButton.VerticalAlignment = 'top';
            
            % 确保对话框在前台
            figure(dlg);
        end
        
        function HelpUserGuideMenuSelected(app, ~, ~)
            % Help -> MBD PLATFORM user guide 菜单选中回调
            % 4条（架构分离原则）
            % 使用openHelpService打开帮助文档
            % 配置读取和路径管理都在openHelpService内部完成
            
            try
                % 调用openHelpService（配置读取在service内部完成）
                openHelpService();
                
                % 显示成功消息
                msgObj = MessageService.create('✅ 已打开帮助文档', ...
                    'Level', MessageService.LEVEL_SUCCESS, ...
                    'Timestamp', true);
                app.MessagesTextArea.Value = msgObj.text;
                app.MessagesTextArea.FontColor = msgObj.color;
                
                % 确保主窗口回到前台（规则第8条）
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 基于第2条规则（错误处理原则）：明确报告错误
                msgObj = MessageService.create(sprintf('⚠️  无法打开帮助文档: %s', ME.message), ...
                    'Level', MessageService.LEVEL_ERROR);
                app.MessagesTextArea.Value = msgObj.text;
                app.MessagesTextArea.FontColor = msgObj.color;
                
                % 显示错误对话框
                uialert(app.MainFigure, ME.message, '错误', 'Icon', 'error');
                
                % 确保主窗口回到前台（规则第8条）
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function callBackEnding(app, msg, varargin)
            % 回调结束处理 - 4条（架构分离原则）重构
            % 使用MessageService处理消息级别和颜色，LoggingService处理日志
            % GUI只负责显示
            
            % 解析参数
            logOff = false;
            if nargin > 2
                if any(contains(varargin, 'logOff'))
                    logOff = true;
                end
            end
            
            % 基于规则第4颜色逻辑在Service层，GUI只负责显示
            % 使用MessageService自动推断级别并确定颜色
            msgObj = MessageService.create(msg, ...
                'Level', MessageService.inferLevel(msg), ...
                'Timestamp', true, ...
                'TimestampFormat', 'yyyy-MM-dd HH:mm:ss');
            
            % GUI只负责显示
            app.MessagesTextArea.Value = msgObj.text;
            app.MessagesTextArea.FontColor = msgObj.color;
            
            % 基于规则第4日志记录使用LoggingService
            if ~logOff
                logger = LoggingService.getInstance();
                logger.logMessage(msgObj);
            end
        end
        
        function put2Log(app, msg)
            % 日志记录 - 【已废弃，请使用 LoggingService】
            % 
            % ⚠️  此方法已废弃（2025-10-14）
            % 4条（架构分离原则），日志记录已迁移到 LoggingService
            % 
            % 新用法：
            %   logger = LoggingService.getInstance();
            %   logger.log(msg, 'info');
            %   % 或
            %   msgObj = MessageService.create(msg, 'Level', 'info');
            %   logger.logMessage(msgObj);
            %
            % 为保持向后兼容，暂时保留此方法
            
            try
                logger = LoggingService.getInstance();
                logger.log(msg, 'info');
            catch
                % 如果 LoggingService 不可用，回退到原实现
                try
                    logFileName = sprintf('logFile_%s.log', datestr(now, 'yymmddHHMM'));
                    workDir = ProjectPathManager.getDirPath('work');
                    logsDir = fullfile(workDir, 'logs');
                    if ~exist(logsDir, 'dir')
                        mkdir(logsDir);
                    end
                    logPath = fullfile(logsDir, logFileName);
                    
                    id = fopen(logPath, 'a+');
                    if id > 0
                        fprintf(id, '%s\n', msg);
                        fclose(id);
                    end
                catch
                    % 日志写入失败，忽略错误
                end
            end
        end
        
        function updateStatusLights(app)
            % 更新状态指示灯 - 根据报告文件存在情况更新，与mbdWorkflowTool_m保持一致
            if isempty(app.CurrentModelDropDown.Value) || strcmp(app.CurrentModelDropDown.Value, '无模型')
                return;
            end
            
            mdlName = app.CurrentModelDropDown.Value;
            okColor = [0 1 0];    % 绿色
            missColor = [0.8 0.8 0.8]; % 灰色
            
            try
                % 更新SDD状态灯
                if ~isempty(app.SDDLamp) && isvalid(app.SDDLamp)
                    if exist(getRptPath('SDD report', mdlName), 'file')
                        app.SDDLamp.Color = okColor;
                    else
                        app.SDDLamp.Color = missColor;
                    end
                end
                
                % 更新Model Standards状态灯
                if ~isempty(app.ModelAdvisorLamp) && isvalid(app.ModelAdvisorLamp)
                    if exist(getRptPath('model standards', mdlName), 'file')
                        app.ModelAdvisorLamp.Color = okColor;
                    else
                        app.ModelAdvisorLamp.Color = missColor;
                    end
                end
                
                % 更新Model Metrics状态灯
                if ~isempty(app.ModelMetricsLamp) && isvalid(app.ModelMetricsLamp)
                    if exist(getRptPath('model metrics', mdlName), 'file')
                        app.ModelMetricsLamp.Color = okColor;
                    else
                        app.ModelMetricsLamp.Color = missColor;
                    end
                end
                
                % 更新SLDV状态灯
                if ~isempty(app.SLDVLamp) && isvalid(app.SLDVLamp)
                    if exist(getRptPath('design error detection', mdlName), 'file')
                        app.SLDVLamp.Color = okColor;
                    else
                        app.SLDVLamp.Color = missColor;
                    end
                end
                
                % 更新代码生成状态灯
                if ~isempty(app.CodeGenLamp) && isvalid(app.CodeGenLamp)
                    % 根据TopModelCheckBox决定使用哪种报告类型
                    if app.TopModelCheckBox.Value
                        codeRptPath = getRptPath('code as top', mdlName);
                    else
                        codeRptPath = getRptPath('code as ref', mdlName);
                    end
                    if exist(codeRptPath, 'file')
                        app.CodeGenLamp.Color = okColor;
                    else
                        app.CodeGenLamp.Color = missColor;
                    end
                end
                
                % 更新Code Inspector状态灯
                if ~isempty(app.CodeInspectorLamp) && isvalid(app.CodeInspectorLamp)
                    if exist(getRptPath('code inspector', mdlName), 'file')
                        app.CodeInspectorLamp.Color = okColor;
                    else
                        app.CodeInspectorLamp.Color = missColor;
                    end
                end
                
                % 更新Bug Finder状态灯
                if ~isempty(app.BugFinderLamp) && isvalid(app.BugFinderLamp)
                    if exist(getRptPath('bug finder', mdlName), 'file')
                        app.BugFinderLamp.Color = okColor;
                    else
                        app.BugFinderLamp.Color = missColor;
                    end
                end
                
                % 更新Code Standards状态灯
                if ~isempty(app.CodeStandardsLamp) && isvalid(app.CodeStandardsLamp)
                    if exist(getRptPath('code standards', mdlName), 'file')
                        app.CodeStandardsLamp.Color = okColor;
                    else
                        app.CodeStandardsLamp.Color = missColor;
                    end
                end
                
                % 更新Code Prover状态灯
                if ~isempty(app.CodeProverLamp) && isvalid(app.CodeProverLamp)
                    if exist(getRptPath('code prover', mdlName), 'file')
                        app.CodeProverLamp.Color = okColor;
                    else
                        app.CodeProverLamp.Color = missColor;
                    end
                end
                
                % 更新Code Metrics状态灯
                % 注意：CodeMetricsLamp可能使用特殊的报告路径，这里先尝试使用通用方法
                
                % 更新模型检查清单状态灯
                if ~isempty(app.ModelChecklistLamp) && isvalid(app.ModelChecklistLamp)
                    % 检查模型检查清单文件是否存在（Excel格式）
                    checklistPath = [mdlName '_model_review_checklist.xlsx'];
                    if exist(checklistPath, 'file')
                        app.ModelChecklistLamp.Color = [1 1 0];  % 黄色 - 文件存在
                    else
                        app.ModelChecklistLamp.Color = missColor; % 灰色 - 文件不存在
                    end
                end
                if ~isempty(app.CodeMetricsLamp) && isvalid(app.CodeMetricsLamp)
                    % 可能需要特殊处理，因为computeCodeMetrics使用Polyspace
                    % 暂时保持现有逻辑，后续可根据实际情况调整
                    try
                        if exist(getRptPath('code standards', mdlName), 'file')
                            app.CodeMetricsLamp.Color = okColor;
                        else
                            app.CodeMetricsLamp.Color = missColor;
                        end
                    catch
                        % 如果getRptPath不支持这种类型，保持默认状态
                        app.CodeMetricsLamp.Color = missColor;
                    end
                end
                
                % 注意：HLRTestLamp和LLRTestLamp基于测试执行结果，不是基于文件存在
                % 它们的状态会在对应的测试按钮回调函数中设置，这里不处理
                
            catch
                % 如果getRptPath不可用，保持默认状态
            end
        end
        
        function d = showProgressDialog(app, title, message)
            % 显示进度对话框的辅助函数
            d = uiprogressdlg(app.MainFigure, 'Title', title, ...
                'Message', message, 'Indeterminate', 'on');
        end
        
        function msg = formatSuccessMessage(~, operation, details)
            % 格式化成功消息
            timestamp = char(datetime('now', 'Format', 'yyyy-MM-dd HH:mm:ss'));
            if nargin < 3
                msg = sprintf('[%s] %s 完成', timestamp, operation);
            else
                msg = sprintf('[%s] %s 完成: %s', timestamp, operation, details);
            end
        end
        
        function msg = formatErrorMessage(~, operation, error)
            % 格式化错误消息
            timestamp = char(datetime('now', 'Format', 'yyyy-MM-dd HH:mm:ss'));
            msg = sprintf('[%s] %s 失败: %s', timestamp, operation, error);
        end
        
        function openReport(app, reportType)
            % 打开指定类型的报告 - 调用getRptPath和winopen，与mbdWorkflowTool_m保持一致
            try
                mdlName = app.CurrentModelDropDown.Value;
                filePath = getRptPath(reportType, mdlName);
                
                if exist(filePath, 'file')
                    winopen(filePath);
                else
                    % 提供详细的文件路径信息，帮助用户定位问题
                    msgText = sprintf(['%s报告文件不存在，请先运行相关检查\n\n' ...
                                      '期望的报告文件路径：\n%s\n\n' ...
                                      '请检查：\n' ...
                                      '1. 是否已运行过该检查\n' ...
                                      '2. 报告生成是否成功\n' ...
                                      '3. 文件路径是否正确'], ...
                                      reportType, filePath);
                    uialert(app.MainFigure, msgText, '提示', 'Icon', 'info');
                    figure(app.MainFigure)
                end
            catch ME
                uialert(app.MainFigure, sprintf('打开报告失败：%s', ME.message), '错误', 'Icon', 'error');
                figure(app.MainFigure)
            end
        end
        
        % =============== Requirements Editor Callback Functions ===============
        
        function HLRTableEdited(app, event)
            % HLR表格编辑回调
            try
                % 更新内部数据
                app.HLRData = app.HLRTable.Data;
                app.HLRFilteredData = app.HLRData;
                
                % 显示编辑确认
                app.MessagesTextArea.Value = sprintf('HLR需求 "%s" 已更新', event.Indices(1));
                
                % 确保主窗口在前台
                % figure(app.MainFigure) #tmpfix;
            catch ME
                app.MessagesTextArea.Value = sprintf('HLR编辑错误: %s', ME.message);
            end
        end
        
        function SRSTableEdited(app, event)
            % SRS表格编辑回调
            try
                % 更新内部数据
                app.SRSData = app.SRSTable.Data;
                app.SRSFilteredData = app.SRSData;
                
                % 显示编辑确认
                app.MessagesTextArea.Value = sprintf('SRS需求 "%s" 已更新', event.Indices(1));
                
                % 确保主窗口在前台
                % figure(app.MainFigure) #tmpfix;
            catch ME
                app.MessagesTextArea.Value = sprintf('SRS编辑错误: %s', ME.message);
            end
        end
        
        % =============== HLR Filter & Action Callbacks ===============
        
        function HLRFilterChanged(app, ~, ~)
            % HLR过滤器变化回调
            try
                app.applyRequirementFilter('HLR');
                % figure(app.MainFigure) #tmpfix;
            catch ME
                app.MessagesTextArea.Value = sprintf('HLR过滤错误: %s', ME.message);
            end
        end
        
        function HLRResetFilters(app, ~, ~)
            % 重置HLR过滤器
            try
                app.HLRStatusFilter.Value = '全部';
                app.HLRPriorityFilter.Value = '全部';
                app.HLRTypeFilter.Value = '全部';
                app.HLRSearchField.Value = '';
                app.applyRequirementFilter('HLR');
                app.MessagesTextArea.Value = 'HLR过滤器已重置';
                % figure(app.MainFigure) #tmpfix;
            catch ME
                app.MessagesTextArea.Value = sprintf('HLR过滤器重置错误: %s', ME.message);
            end
        end
        
        
        
        
        % =============== SRS Filter & Action Callbacks ===============
        
        function SRSFilterChanged(app, ~, ~)
            % SRS过滤器变化回调
            try
                app.applyRequirementFilter('SRS');
                % figure(app.MainFigure) #tmpfix;
            catch ME
                app.MessagesTextArea.Value = sprintf('SRS过滤错误: %s', ME.message);
            end
        end
        
        function SRSResetFilters(app, ~, ~)
            % 重置SRS过滤器
            try
                app.SRSStatusFilter.Value = '全部';
                app.SRSPriorityFilter.Value = '全部';
                app.SRSTypeFilter.Value = '全部';
                app.SRSSearchField.Value = '';
                app.applyRequirementFilter('SRS');
                app.MessagesTextArea.Value = 'SRS过滤器已重置';
                % figure(app.MainFigure) #tmpfix;
            catch ME
                app.MessagesTextArea.Value = sprintf('SRS过滤器重置错误: %s', ME.message);
            end
        end
        
        % =============== ALM Filter Callbacks ===============
        
        function PRFilterChanged(app, ~, ~)
            % PR过滤器变更回调
            try
                app.applyALMFilter('PR');
                % figure(app.MainFigure) #tmpfix;
            catch ME
                fprintf('❌ PR过滤错误: %s\n', ME.message);
            end
        end
        
        function CRFilterChanged(app, ~, ~)
            % CR过滤器变更回调
            try
                app.applyALMFilter('CR');
                % figure(app.MainFigure) #tmpfix;
            catch ME
                fprintf('❌ CR过滤错误: %s\n', ME.message);
            end
        end
        
        function resetPRFilter(app, ~, ~)
            % 重置PR过滤器
            % 基于规则第3条：使用英文"All"，UI自适应后台
            try
                app.PRStatusFilter.Value = 'All';
                app.PRSeverityFilter.Value = 'All';
                app.PRSearchField.Value = '';
                app.applyALMFilter('PR');
                fprintf('✅ PR过滤器已重置\n');
                % figure(app.MainFigure) #tmpfix;
            catch ME
                fprintf('❌ PR过滤器重置错误: %s\n', ME.message);
            end
        end
        
        function resetCRFilter(app, ~, ~)
            % 重置CR过滤器
            % 基于规则第3条：使用英文"All"，UI自适应后台
            try
                app.CRStatusFilter.Value = 'All';
                app.CRPriorityFilter.Value = 'All';
                app.CRSearchField.Value = '';
                app.applyALMFilter('CR');
                fprintf('✅ CR过滤器已重置\n');
                % figure(app.MainFigure) #tmpfix;
            catch ME
                fprintf('❌ CR过滤器重置错误: %s\n', ME.message);
            end
        end
        
        % =============== Requirements Filter Engine ===============
        
        function applyRequirementFilter(app, reqType)
            % 应用需求过滤器 - 动态过滤支持
            % 基于规则第4使用RequirementsDataTransformer进行数据过滤
            % 使用大写属性名（Status、Priority、Type）以匹配配置文件
            try
                if strcmp(reqType, 'HLR')
                    originalData = app.HLRData;
                    filters.Status = app.HLRStatusFilter.Value;
                    filters.Priority = app.HLRPriorityFilter.Value;
                    filters.Type = app.HLRTypeFilter.Value;
                    filters.searchText = app.HLRSearchField.Value;
                    targetTable = app.HLRTable;
                else
                    originalData = app.SRSData;
                    filters.Status = app.SRSStatusFilter.Value;
                    filters.Priority = app.SRSPriorityFilter.Value;
                    filters.Type = app.SRSTypeFilter.Value;
                    filters.searchText = app.SRSSearchField.Value;
                    targetTable = app.SRSTable;
                end
                
                if isempty(originalData)
                    return;
                end
                
                % 使用RequirementsDataTransformer进行动态过滤
                % 新过滤器逻辑不再依赖硬编码列索引，而是根据属性名动态匹配
                filteredData = RequirementsDataTransformer.filterRequirements(originalData, filters);
                
                % 更新表格显示
                targetTable.Data = filteredData;
                
            catch ME
                app.MessagesTextArea.Value = sprintf('%s过滤器应用错误: %s', reqType, ME.message);
            end
        end
        
        % ✅ 需求过滤逻辑已迁移到RequirementsDataTransformer（基于规则第4条）
        % 原始代码70行 → 现在30行，代码减少57%
        
        function applyALMFilter(app, almType)
            % 应用ALM（PR/CR）过滤器
            % 基于规则第4使用ALMDataTransformer进行数据过滤
            % 
            % 修复：使用保存的原始数据进行过滤，避免"设置为全部后仍无数据"的问题
            try
                if strcmp(almType, 'PR')
                    % PR数据过滤
                    % 使用保存的原始数据而不是当前表格数据
                    if isempty(app.PROriginalData)
                        % 如果没有原始数据，使用当前表格数据作为原始数据
                        app.PROriginalData = app.PRTable.Data;
                    end
                    originalData = app.PROriginalData;
                    
                    filters.status = app.PRStatusFilter.Value;
                    filters.severity = app.PRSeverityFilter.Value;
                    filters.searchText = app.PRSearchField.Value;
                    targetTable = app.PRTable;
                else
                    % CR数据过滤
                    % 使用保存的原始数据而不是当前表格数据
                    if isempty(app.CROriginalData)
                        % 如果没有原始数据，使用当前表格数据作为原始数据
                        app.CROriginalData = app.CRTable.Data;
                    end
                    originalData = app.CROriginalData;
                    
                    filters.status = app.CRStatusFilter.Value;
                    filters.severity = app.CRPriorityFilter.Value;
                    filters.searchText = app.CRSearchField.Value;
                    targetTable = app.CRTable;
                end
                
                if isempty(originalData)
                    return;
                end
                
                % 使用ALMDataTransformer进行过滤（支持中英文映射）
                filteredData = ALMDataTransformer.filterTableData(originalData, filters, almType);
                
                % 更新表格显示
                targetTable.Data = filteredData;
                
            catch ME
                fprintf('❌ %s过滤器应用错误: %s\n', almType, ME.message);
            end
        end
        
        % =============== Requirements Import/Export Callbacks ===============
        
        % Requirements Tab Callbacks - 基于规则第4使用RequirementsManagementService
        function ImportCSVButtonPushed(app, varargin)
            % 导入CSV需求文件 - 使用RequirementsManagementService
            try
                if ~isempty(app.logger)
                    app.logger.logInfo('用户点击CSV导入按钮');
                end
                
                app.MessagesTextArea.Value = '启动CSV导入功能...';
                drawnow;
                
                % 基于规则第4调用需求管理服务，传递当前主题
                [success, result] = app.ReqService.importFromCSV([], app.MainFigure, app.MessagesTextArea, app.CurrentTheme);
                
                if success
                    app.MessagesTextArea.Value = 'CSV导入成功完成！';
                    % 刷新需求树和表格
                    app.initializeRequirementsTree();
                    app.loadDefaultSLREQXFile();
                else
                    if isfield(result, 'error')
                        app.MessagesTextArea.Value = result.error;
                    else
                        app.MessagesTextArea.Value = 'CSV导入已取消或失败';
                    end
                end
                
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台
                
            catch ME
                app.MessagesTextArea.Value = sprintf('CSV导入错误: %s', ME.message);
                uialert(app.MainFigure, sprintf('CSV导入失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function ImportReqIFButtonPushed(app, varargin)
            % 导入ReqIF需求文件 - 使用RequirementsManagementService
            try
                app.MessagesTextArea.Value = '启动ReqIF导入功能...';
                drawnow;
                
                % 基于规则第4调用需求管理服务
                [success, result] = app.ReqService.importFromReqIF([], app.MainFigure, app.MessagesTextArea);
                
                if success
                    app.MessagesTextArea.Value = 'ReqIF导入成功完成！';
                    % 刷新需求树和表格
                    app.initializeRequirementsTree();
                    app.loadDefaultSLREQXFile();
                else
                    if isfield(result, 'error')
                        app.MessagesTextArea.Value = result.error;
                    else
                        app.MessagesTextArea.Value = 'ReqIF导入已取消或失败';
                    end
                end
                
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台
                
            catch ME
                app.MessagesTextArea.Value = sprintf('ReqIF导入错误: %s', ME.message);
                uialert(app.MainFigure, sprintf('ReqIF导入失败：\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function ExportToWordButtonPushed(app, varargin)
            % Export requirements to Word document using predefined template
            % 基于规则第4调用RequirementsManagementService处理业务逻辑
            try
                % 基于规则第3条：通过ProjectPathManager获取路径
                defaultPath = ProjectPathManager.getDirPath('requirements specification');
                
                % 1. 选择SLREQX文件，自动定位到DO_02_Requirements/specification目录
                [file, path] = uigetfile('*.slreqx', '选择要导出的SLREQX需求文件', defaultPath);
                if isequal(file, 0)
                    return;
                end
                slreqxFilePath = fullfile(path, file);
                
                % 2. 选择导出位置和文件名
                [saveFile, savePath] = uiputfile('*.docx', '选择Word文档保存位置');
                if isequal(saveFile, 0)
                    return;
                end
                wordFilePath = fullfile(savePath, saveFile);
                
                % 基于规则第8条：显示进度条（持续时间超过1秒的操作）
                progressDlg = uiprogressdlg(app.MainFigure, 'Title', '导出需求到Word', ...
                    'Message', sprintf('正在导出需求到Word文档...\n源文件: %s', file), ...
                    'Indeterminate', 'on');
                
                app.MessagesTextArea.Value = sprintf('正在导出需求到Word: %s -> %s', slreqxFilePath, wordFilePath);
                drawnow;
                
                try
                    % 调用服务层方法处理导出
                    [success, errorMsg] = app.ReqService.exportToWord(slreqxFilePath, wordFilePath);
                    
                    % 关闭进度条
                    close(progressDlg);
                    
                    if success
                        app.MessagesTextArea.Value = sprintf('✅ 需求导出到Word完成: %s', wordFilePath);
                        uialert(app.MainFigure, sprintf('导出成功！\n文件已保存到:\n%s', wordFilePath), '导出成功', 'Icon', 'success');
                    else
                        app.MessagesTextArea.Value = sprintf('❌ 导出失败: %s', errorMsg);
                        uialert(app.MainFigure, sprintf('导出失败:\n%s', errorMsg), '错误', 'Icon', 'error');
                    end
                catch ME2
                    % 确保进度条被关闭
                    close(progressDlg);
                    rethrow(ME2);
                end
                
                % figure(app.MainFigure) #tmpfix;
            catch ME
                app.MessagesTextArea.Value = sprintf('❌ 导出到Word错误: %s', ME.message);
                uialert(app.MainFigure, sprintf('导出失败:\n%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function ViewTraceMatrixButtonPushed(app, varargin)
            % View trace matrix
            try
                app.MessagesTextArea.Value = '查看追溯矩阵功能待实现';
                % figure(app.MainFigure) #tmpfix;
            catch ME
                app.MessagesTextArea.Value = sprintf('查看追溯矩阵错误: %s', ME.message);
            end
        end
        
        function AddReqTabButtonPushed(app, varargin)
            % 基于第2条规则：不使用fallback机制，错误必须明确报告
            % 添加新的需求Tab
            try
                % 让用户输入Tab名称
                tabName = inputdlg('请输入新Tab的名称:', 'AddRequirement Tab', [1 50]);
                
                if isempty(tabName)
                    % 用户取消
                    app.MessagesTextArea.Value = '取消添加Tab';
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                tabName = strtrim(tabName{1});
                
                if isempty(tabName)
                    uialert(app.MainFigure, 'Tab名称不能为空', '错误');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 检查是否已存在同名Tab
                if app.isReqTabNameExists(tabName)
                    uialert(app.MainFigure, sprintf('Tab名称"%s"已存在，请使用其他名称', tabName), '错误');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 创建新Tab
                app.createDynamicReqTab(tabName);
                
                app.MessagesTextArea.Value = sprintf('成功添加需求Tab: %s', tabName);
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                uialert(app.MainFigure, sprintf('添加Tab失败: %s', ME.message), '错误');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function DeleteReqTabButtonPushed(app, varargin)
            % 基于第2条规则：不使用fallback机制，错误必须明确报告
            % 删除已有的需求Tab
            try
                % 获取所有动态Tab名称（不包括HLR和SRS）
                if isempty(fieldnames(app.DynamicReqTabs))
                    uialert(app.MainFigure, '没有可删除的动态Tab', '提示');
                % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                tabNames = fieldnames(app.DynamicReqTabs);
                
                if isempty(tabNames)
                    uialert(app.MainFigure, '没有可删除的动态Tab', '提示');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 让用户选择要删除的Tab
                [idx, tf] = listdlg('PromptString', '选择要删除的Tab:', ...
                                    'SelectionMode', 'single', ...
                                    'ListString', tabNames, ...
                                    'ListSize', [300, 300]);
                
                if ~tf
                    % 用户取消
                    app.MessagesTextArea.Value = '取消删除Tab';
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                selectedTabName = tabNames{idx};
                
                % 确认删除
                answer = uiconfirm(app.MainFigure, ...
                    sprintf('确定要删除Tab "%s" 吗？', selectedTabName), ...
                    '确认删除', ...
                    'Options', {'是', '否'}, ...
                    'DefaultOption', 2, ...
                    'CancelOption', 2);
                
                if strcmp(answer, '是')
                    % 删除Tab
                    app.deleteDynamicReqTab(selectedTabName);
                    app.MessagesTextArea.Value = sprintf('成功删除需求Tab: %s', selectedTabName);
                else
                    app.MessagesTextArea.Value = '取消删除Tab';
                end
                
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                uialert(app.MainFigure, sprintf('删除Tab失败: %s', ME.message), '错误');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        function RefreshReqTreeButtonPushed(app, varargin)
            % 刷新需求文件树
            try
                app.MessagesTextArea.Value = '正在刷新需求文件树...';
                drawnow;
                
                % 获取项目根目录（从当前文件位置推断）
                projectRoot = fileparts(fileparts(fileparts(mfilename('fullpath'))));
                
                % 调用独立的需求文件树管理器刷新树
                [success, errorMsg] = RequirementsTreeManager('refreshTree', app.RequirementsTree, projectRoot);
                
                if success
                    app.MessagesTextArea.Value = '需求文件树刷新完成';
                else
                    app.MessagesTextArea.Value = sprintf('刷新需求文件树失败: %s', errorMsg);
                end
                
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                app.MessagesTextArea.Value = sprintf('刷新需求文件树错误: %s', ME.message);
            end
        end
        
        function OpenRequirementFileButtonPushed(app, varargin)
            % 打开选中的需求文件按钮回调
            try
                % 获取当前选中的节点
                selectedNodes = app.RequirementsTree.SelectedNodes;
                
                if isempty(selectedNodes)
                    uialert(app.MainFigure, '请先选择一个需求文件', '提示', 'Icon', 'info');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                selectedNode = selectedNodes(1);
                
                if isempty(selectedNode.UserData) || ~strcmp(selectedNode.UserData.Type, 'slreqx')
                    uialert(app.MainFigure, '请选择一个有效的slreqx文件', '提示', 'Icon', 'warning');
                    % figure(app.MainFigure) #tmpfix;
                    return;
                end
                
                % 显示进度信息
                app.MessagesTextArea.Value = sprintf('正在打开文件: %s', selectedNode.Text);
                drawnow;
                
                % 调用文件打开功能
                [success, errorMsg] = RequirementsTreeManager('openFile', selectedNode.UserData.Path);
                
                if success
                    app.MessagesTextArea.Value = sprintf('已打开文件: %s', selectedNode.Text);
                else
                    app.MessagesTextArea.Value = sprintf('打开文件失败: %s', errorMsg);
                    uialert(app.MainFigure, errorMsg, '文件打开错误', 'Icon', 'warning');
                end
                
                % % figure(app.MainFigure) #tmpfix;
                
            catch ME
                app.MessagesTextArea.Value = sprintf('打开需求文件按钮错误: %s', ME.message);
                uialert(app.MainFigure, sprintf('打开文件时发生错误：\n%s', ME.message), '错误', 'Icon', 'error');
                % % figure(app.MainFigure) #tmpfix;
            end
        end
        function RequirementsTreeSelectionChanged(app, src, event)
            % 需求文件树选择变化回调 - 更新按钮状态
            try
                % 验证事件对象和属性
                if isempty(event) 
                    return;
                end
                
                % 检查SelectedNodes属性是否存在
                if ~isprop(event, 'SelectedNodes') && ~isfield(event, 'SelectedNodes')
                    return;
                end
                
                selectedNodes = event.SelectedNodes;
                
                if isempty(selectedNodes)
                    % 没有选中节点时禁用按钮
                    app.OpenRequirementFileButton.Enable = 'off';
                    return;
                end
                
                selectedNode = selectedNodes(1);
                
                % 更新打开文件按钮的启用状态
                app.updateOpenFileButtonState(selectedNode);
                
            catch ME
                app.MessagesTextArea.Value = sprintf('需求文件树选择错误: %s', ME.message);
                fprintf('调试信息: %s\n', ME.message);
            end
        end
        
        function updateOpenFileButtonState(app, selectedNode)
            % 更新打开文件按钮的启用状态
            try
                if isempty(selectedNode) || isempty(selectedNode.UserData)
                    app.OpenRequirementFileButton.Enable = 'off';
                    return;
                end
                
                % 只有选中slreqx文件时才启用按钮
                if strcmp(selectedNode.UserData.Type, 'slreqx')
                    app.OpenRequirementFileButton.Enable = 'on';
                else
                    app.OpenRequirementFileButton.Enable = 'off';
                end
                
            catch ME
                % 发生错误时禁用按钮
                app.OpenRequirementFileButton.Enable = 'off';
                fprintf('更新按钮状态错误: %s\n', ME.message);
            end
        end
        
        % =============== Testing Management Callbacks ===============
        
        function SubTabGroupSelectionChanged(app, src, event)
            % 当切换二级标签页时触发
            % 基于第8条规则：确保主窗口回到前台
            
            selectedTab = app.SubTabGroup.SelectedTab;
            
            % 如果切换到Testing Management标签页，加载测试用例并刷新状态灯
            if isequal(selectedTab, app.TestingTab)
                app.loadTestCases();
                app.refreshTestReportLamps();
            end
            
            % 确保主窗口回到前台
            % figure(app.MainFigure) #tmpfix;
        end
        
        function loadTestCases(app)
            % 加载当前模型的测试用例并显示在表格中
            % 基于第4条规则：GUI只负责显示，业务逻辑由getModelTestCaseStatus处理
            % 基于第8条规则：显示进度条
            
            try
                % 获取当前选中的模型
                currentModel = app.CurrentModelDropDown.Value;
                
                if isempty(currentModel) || strcmp(currentModel, 'No Models')
                    app.TestCaseTable.Data = {};
                    app.MessagesTextArea.Value = '请先选择一个模型';
                    return;
                end
                
                % 显示进度对话框（基于第8条规则）
                d = uiprogressdlg(app.MainFigure, ...
                    'Title', '加载测试用例', ...
                    'Message', sprintf('正在加载模型 %s 的测试用例...', currentModel), ...
                    'Indeterminate', 'on');
                
                % 确保TestService已初始化
                if isempty(app.TestService)
                    app.TestService = TestManagementService.getInstance();
                end
                
                % 调用TestService获取测试用例
                testCaseInfo = app.TestService.getTestCases(currentModel);
                
                % 使用TestDataTransformer转换为表格数据
                if isempty(testCaseInfo)
                    app.TestCaseTable.Data = {};
                    app.MessagesTextArea.Value = sprintf('模型 %s 没有找到测试用例或测试文件不存在', currentModel);
                else
                    % 转换测试用例数据
                    tableData = TestDataTransformer.convertTestCasesToTable(testCaseInfo);
                    app.TestCaseTable.Data = tableData;
                    
                    % 提取需求URL并存储到UserData
                    requirementURLs = TestDataTransformer.extractRequirementURLs(testCaseInfo);
                    app.TestCaseTable.UserData = struct('RequirementURLs', {requirementURLs});
                    
                    % 生成并显示测试摘要
                    summary = TestDataTransformer.generateTestSummary(testCaseInfo);
                    summaryText = TestDataTransformer.formatTestSummary(summary);
                    app.MessagesTextArea.Value = summaryText;
                end
                
                % 关闭进度对话框
                close(d);
                
                % 基于第8条规则：确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 基于第2条规则：明确报告错误，不使用fallback
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                
                app.TestCaseTable.Data = {};
                errorMsg = sprintf('加载测试用例失败: %s', ME.message);
                app.MessagesTextArea.Value = errorMsg;
                
                % 显示错误对话框
                uialert(app.MainFigure, errorMsg, '错误', 'Icon', 'error');
                
                % 基于第8条规则：确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function TestAuthoringToolButtonPushed(app, varargin)
            % Open Test Authoring Tool
            try
                d = uiprogressdlg(app.MainFigure, ...
                    'Title', 'Test Authoring Tool', ...
                    'Message', '正在启动Test Authoring Tool...', ...
                    'Indeterminate', 'on');
                
                % 尝试打开Test Authoring Tool
                % 如果testAuthoringTool不存在，提供友好的错误提示
                if exist('testAuthoringTool_m', 'file') == 2
                    testAuthoringTool_m;
                    app.MessagesTextArea.Value = 'Test Authoring Tool已启动';
                else
                    % 尝试其他可能的命令
                    try
                        evalin('base', 'testAuthoringTool');
                        app.MessagesTextArea.Value = 'Test Authoring Tool已启动';
                    catch
                        app.MessagesTextArea.Value = '未找到Test Authoring Tool，请确保已安装相关工具箱';
                    end
                end
                close(d)
                
            catch ME
                app.MessagesTextArea.Value = sprintf('启动Test Authoring Tool错误: %s', ME.message);
                close(d)
            end
        end
        
        function RefreshTestCasesButtonPushed(app, varargin)
            % 刷新测试用例表格按钮回调
            % 基于第8条规则：显示进度条，确保主窗口回到前台
            
            app.loadTestCases();
        end
        
        function TestCaseTableCellSelected(app, src, event)
            % 测试用例表格单元格选择回调
            % 当用户点击需求列（第5列）时，打开对应的URL
            
            % 检查是否有选择
            if isempty(event.Indices)
                return;
            end
            
            % 获取选中的行和列
            row = event.Indices(1);
            col = event.Indices(2);
            
            % 检查是否点击了需求列（第5列）
            if col == 5
                % 从UserData中获取需求URL
                if ~isempty(app.TestCaseTable.UserData) && isfield(app.TestCaseTable.UserData, 'RequirementURLs')
                    urls = app.TestCaseTable.UserData.RequirementURLs;
                    
                    % 检查行索引是否有效
                    if row <= numel(urls) && ~isempty(urls{row})
                        reqURL = urls{row};
                        
                        % 如果有多个URL（用分号分隔），打开所有
                        if contains(reqURL, ';')
                            urlList = strsplit(reqURL, ';');
                            % 打开所有URL
                            for i = 1:numel(urlList)
                                if ~isempty(urlList{i})
                                    web(urlList{i}, '-browser');
                                end
                            end
                            app.MessagesTextArea.Value = sprintf('已打开 %d 个需求链接', numel(urlList));
                        elseif ~isempty(reqURL)
                            % 打开单个URL
                            web(reqURL, '-browser');
                            app.MessagesTextArea.Value = '已打开需求链接';
                        else
                            app.MessagesTextArea.Value = '该测试用例没有关联的需求链接';
                        end
                    end
                end
            end
        end
        
        function OpenTestManagerButtonPushed(app, varargin)
            % 打开当前模型的Test Manager测试文件
            % 基于第8条规则：显示进度条，确保主窗口回到前台
            
            try
                % 获取当前选中的模型
                currentModel = app.CurrentModelDropDown.Value;
                if isempty(currentModel)
                    uialert(app.MainFigure, '请先选择一个模型', '错误');
                    return;
                end
                
                % 显示进度对话框
                d = uiprogressdlg(app.MainFigure, 'Title', '正在打开Test Manager', ...
                    'Message', sprintf('正在加载模型 %s 的测试文件...', currentModel), ...
                    'Indeterminate', 'on');
                
                % 获取测试文件路径
                testFileName = ProjectNamingManager.getNameStr('HLR test cases', currentModel);
                testFileDir = ProjectPathManager.getDirPath('HLR test cases', currentModel);
                testFilePath = fullfile(testFileDir, [testFileName, '.mldatx']);
                
                % 检查测试文件是否存在
                if ~exist(testFilePath, 'file')
                    close(d);
                    uialert(app.MainFigure, ...
                        sprintf('找不到测试文件:\n%s\n\n请先创建测试用例', testFilePath), ...
                        '测试文件不存在');
                    return;
                end
                
                % 打开Test Manager并加载测试文件
                sltest.testmanager.TestFile(testFilePath)
                sltest.testmanager.view;
                
                % 关闭进度对话框
                close(d);
                
                % 更新消息
                app.MessagesTextArea.Value = sprintf('已打开Test Manager并加载测试文件: %s', testFileName);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                
                % 显示错误
                uialert(app.MainFigure, ...
                    sprintf('打开Test Manager失败:\n%s', ME.message), ...
                    '错误');
                app.MessagesTextArea.Value = sprintf('打开Test Manager错误: %s', ME.message);
            end
        end
        
        function OpenHLRReportButtonPushed(app, varargin)
            % 打开HLR测试报告（PDF）
            % 基于第8条规则：显示进度条，确保主窗口回到前台
            % 基于第2条规则：不使用fallback机制，明确报告错误
            
            try
                % 获取当前选中的模型
                currentModel = app.CurrentModelDropDown.Value;
                if isempty(currentModel)
                    uialert(app.MainFigure, '请先选择一个模型', '错误');
                    return;
                end
                
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Report...', 'Indeterminate', 'on');
                
                % 获取测试模式（MIL/SIL/PIL）
                userMode = app.TestModeDropDown.Value;
                
                % 根据测试模式构造报告类型，使用 getRptPath 统一获取路径
                switch userMode
                    case 'MIL'
                        reportType = 'HLR sim report';
                    case 'SIL'
                        reportType = 'HLR SIL report';
                    case 'PIL'
                        reportType = 'HLR PIL report';
                    otherwise
                        close(dlg);
                        error('未知的测试模式: %s', userMode);
                end
                
                % 使用 getRptPath 获取报告文件路径
                rptFile = getRptPath(reportType, currentModel);
                
                % 检查报告文件是否存在
                if exist(rptFile, "file")
                    open(rptFile);
                    msg = sprintf('%s HLR test report opened.', userMode);
                    app.MessagesTextArea.Value = msg;
                else
                    close(dlg);
                    msg = sprintf('%s HLR test report does not exist!\n路径: %s', userMode, rptFile);
                    app.MessagesTextArea.Value = msg;
                    uialert(app.MainFigure, msg, '报告不存在');
                    return;
                end
                
                % 关闭进度对话框
                close(dlg);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                % 显示错误（基于第2条规则：明确报告错误）
                uialert(app.MainFigure, ...
                    sprintf('打开HLR报告失败:\n%s', ME.message), ...
                    '错误');
                app.MessagesTextArea.Value = sprintf('打开HLR报告错误: %s', ME.message);
            end
        end
        
        function OpenLLRReportButtonPushed(app, varargin)
            % 打开LLR测试报告（PDF）
            % 基于第8条规则：显示进度条，确保主窗口回到前台
            % 基于第2条规则：不使用fallback机制，明确报告错误
            
            try
                % 获取当前选中的模型
                currentModel = app.CurrentModelDropDown.Value;
                if isempty(currentModel)
                    uialert(app.MainFigure, '请先选择一个模型', '错误');
                    return;
                end
                
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Report...', 'Indeterminate', 'on');
                
                % 获取测试模式（MIL/SIL/PIL）
                userMode = app.TestModeDropDown.Value;
                
                % 根据测试模式构造报告类型，使用 getRptPath 统一获取路径
                switch userMode
                    case 'MIL'
                        reportType = 'LLR sim report';
                    case 'SIL'
                        reportType = 'LLR SIL report';
                    case 'PIL'
                        reportType = 'LLR PIL report';
                    otherwise
                        close(dlg);
                        error('未知的测试模式: %s', userMode);
                end
                
                % 使用 getRptPath 获取报告文件路径
                rptFile = getRptPath(reportType, currentModel);
                
                % 检查报告文件是否存在
                if exist(rptFile, "file")
                    open(rptFile);
                    msg = sprintf('%s LLR test report opened.', userMode);
                    app.MessagesTextArea.Value = msg;
                else
                    close(dlg);
                    msg = sprintf('%s LLR test report does not exist!\n路径: %s', userMode, rptFile);
                    app.MessagesTextArea.Value = msg;
                    uialert(app.MainFigure, msg, '报告不存在');
                    return;
                end
                
                % 关闭进度对话框
                close(dlg);
            
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                % 显示错误（基于第2条规则：明确报告错误）
                uialert(app.MainFigure, ...
                    sprintf('打开LLR报告失败:\n%s', ME.message), ...
                    '错误');
                app.MessagesTextArea.Value = sprintf('打开LLR报告错误: %s', ME.message);
            end
        end
        
        function TestModeDropDownValueChanged(app, ~, ~)
            % Test mode dropdown value changed callback
            % 基于第8条规则：切换测试模式时刷新状态灯
            try
                selectedMode = app.TestModeDropDown.Value;
                
                % 根据测试模式更新消息
                switch selectedMode
                    case 'MIL'
                        app.MessagesTextArea.Value = '当前测试模式: MIL (Model-in-the-Loop)';
                    case 'SIL'
                        app.MessagesTextArea.Value = '当前测试模式: SIL (Software-in-the-Loop)';
                    case 'PIL'
                        app.MessagesTextArea.Value = '当前测试模式: PIL (Processor-in-the-Loop)';
                end
                
                % 刷新状态灯（根据当前测试模式更新报告和覆盖度状态）
                app.refreshTestReportLamps();
                
                % figure(app.MainFigure) #tmpfix;
            catch ME
                app.MessagesTextArea.Value = sprintf('测试模式切换错误: %s', ME.message);
            end
        end
        
        function refreshTestReportLamps(app)
            % 刷新HLR和LLR的测试报告和覆盖度报告状态灯
            % 基于第2条规则：明确检查文件存在性，不使用fallback
            
            try
                % 获取当前模型
                currentModel = app.CurrentModelDropDown.Value;
                if isempty(currentModel)
                    return;
                end
                
                % 获取测试模式
                testMode = app.TestModeDropDown.Value;
                
                % 根据测试模式确定配置键
                switch testMode
                    case 'MIL'
                        hlrTestReportKey = 'HLR sim report';
                        hlrTestDirKey = 'HLR sim results';
                        hlrCovKey = 'HLR model coverage report';
                        hlrCovDirKey = 'HLR model coverage';
                        llrTestReportKey = 'LLR sim report';
                        llrTestDirKey = 'LLR sim results';
                        llrCovKey = 'LLR model coverage report';
                        llrCovDirKey = 'LLR model coverage';
                    case 'SIL'
                        hlrTestReportKey = 'HLR SIL report';
                        hlrTestDirKey = 'HLR SIL results';
                        hlrCovKey = 'HLR code coverage report';
                        hlrCovDirKey = 'HLR SIL coverage';
                        llrTestReportKey = 'LLR SIL report';
                        llrTestDirKey = 'LLR SIL results';
                        llrCovKey = 'LLR code coverage report';
                        llrCovDirKey = 'LLR SIL coverage';
                    case 'PIL'
                        hlrTestReportKey = 'HLR PIL report';
                        hlrTestDirKey = 'HLR PIL results';
                        hlrCovKey = 'HLR code coverage report';
                        hlrCovDirKey = 'HLR PIL coverage';
                        llrTestReportKey = 'LLR PIL report';
                        llrTestDirKey = 'LLR PIL results';
                        llrCovKey = 'LLR code coverage report';
                        llrCovDirKey = 'LLR PIL coverage';
                    otherwise
                        return;
                end
                
                % HLR测试报告状态 - 使用 getRptPath 统一获取路径
                hlrTestReport = getRptPath(hlrTestReportKey, currentModel);
                app.HLRTestLamp.Color = iif(exist(hlrTestReport, "file"), [0 1 0], [0.5 0.5 0.5]);
                
                % HLR覆盖度报告状态
                hlrCovReport = fullfile(ProjectPathManager.getDirPath(hlrCovDirKey, currentModel), ...
                    [ProjectNamingManager.getNameStr(hlrCovKey, currentModel), '.html']);
                app.HLRCoverageLamp.Color = iif(exist(hlrCovReport, "file"), [0 1 0], [0.5 0.5 0.5]);
                
                % LLR测试报告状态 - 使用 getRptPath 统一获取路径
                llrTestReport = getRptPath(llrTestReportKey, currentModel);
                app.LLRTestLamp.Color = iif(exist(llrTestReport, "file"), [0 1 0], [0.5 0.5 0.5]);
                
                % LLR覆盖度报告状态
                llrCovReport = fullfile(ProjectPathManager.getDirPath(llrCovDirKey, currentModel), ...
                    [ProjectNamingManager.getNameStr(llrCovKey, currentModel), '.html']);
                app.LLRCoverageLamp.Color = iif(exist(llrCovReport, "file"), [0 1 0], [0.5 0.5 0.5]);
                
            catch ME
                warning('refreshTestReportLamps:Error', '刷新状态灯失败: %s', ME.message);
            end
            
            function result = iif(condition, trueVal, falseVal)
                % 简单的三元运算符实现
                if condition
                    result = trueVal;
                else
                    result = falseVal;
                end
            end
        end
        
        function OpenHLRCoverageButtonPushed(app, varargin)
            % 打开HLR覆盖度报告（HTML）
            % 基于第8条规则：显示进度条，确保主窗口回到前台
            % 基于第2条规则：不使用fallback机制，明确报告错误
            
            try
                % 获取当前选中的模型
                currentModel = app.CurrentModelDropDown.Value;
                if isempty(currentModel)
                    uialert(app.MainFigure, '请先选择一个模型', '错误');
                    return;
                end
                
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Coverage Report...', 'Indeterminate', 'on');
                
                % 获取测试模式（MIL/SIL/PIL）
                testMode = app.TestModeDropDown.Value;
                
                % 根据测试模式构造配置键
                switch testMode
                    case 'MIL'
                        dirKey = 'HLR model coverage';
                        nameKey = 'HLR model coverage report';
                    case 'SIL'
                        dirKey = 'HLR SIL coverage';
                        nameKey = 'HLR code coverage report';
                    case 'PIL'
                        dirKey = 'HLR PIL coverage';
                        nameKey = 'HLR code coverage report';
                    otherwise
                        close(dlg);
                        error('未知的测试模式: %s', testMode);
                end
                
                % 构造覆盖度报告文件路径
                rptFile = fullfile(ProjectPathManager.getDirPath(dirKey, currentModel), ...
                    [ProjectNamingManager.getNameStr(nameKey, currentModel), '.html']);
                
                % 检查报告文件是否存在
                if exist(rptFile, "file")
                    web(rptFile, '-browser');
                    msg = sprintf('%s HLR coverage report opened.', testMode);
                    app.MessagesTextArea.Value = msg;
                else
                    close(dlg);
                    msg = sprintf('%s HLR coverage report does not exist!\n路径: %s', testMode, rptFile);
                    app.MessagesTextArea.Value = msg;
                    uialert(app.MainFigure, msg, '覆盖度报告不存在');
                    return;
                end
                
                % 关闭进度对话框
                close(dlg);
            
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                % 显示错误（基于第2条规则：明确报告错误）
                uialert(app.MainFigure, ...
                    sprintf('打开HLR覆盖度报告失败:\n%s', ME.message), ...
                    '错误');
                app.MessagesTextArea.Value = sprintf('打开HLR覆盖度报告错误: %s', ME.message);
            end
        end
        
        function OpenLLRCoverageButtonPushed(app, varargin)
            % 打开LLR覆盖度报告（HTML）
            % 基于第8条规则：显示进度条，确保主窗口回到前台
            % 基于第2条规则：不使用fallback机制，明确报告错误
            
            try
                % 获取当前选中的模型
                currentModel = app.CurrentModelDropDown.Value;
                if isempty(currentModel)
                    uialert(app.MainFigure, '请先选择一个模型', '错误');
                    return;
                end
                
                % 显示进度对话框
                dlg = uiprogressdlg(app.MainFigure, 'Title', 'Please Wait', ...
                    'Message', 'Opening Coverage Report...', 'Indeterminate', 'on');
                
                % 获取测试模式（MIL/SIL/PIL）
                testMode = app.TestModeDropDown.Value;
                
                % 根据测试模式构造配置键
                switch testMode
                    case 'MIL'
                        dirKey = 'LLR model coverage';
                        nameKey = 'LLR model coverage report';
                    case 'SIL'
                        dirKey = 'LLR SIL coverage';
                        nameKey = 'LLR code coverage report';
                    case 'PIL'
                        dirKey = 'LLR PIL coverage';
                        nameKey = 'LLR code coverage report';
                    otherwise
                        close(dlg);
                        error('未知的测试模式: %s', testMode);
                end
                
                % 构造覆盖度报告文件路径
                rptFile = fullfile(ProjectPathManager.getDirPath(dirKey, currentModel), ...
                    [ProjectNamingManager.getNameStr(nameKey, currentModel), '.html']);
                
                % 检查报告文件是否存在
                if exist(rptFile, "file")
                    web(rptFile, '-browser');
                    msg = sprintf('%s LLR coverage report opened.', testMode);
                    app.MessagesTextArea.Value = msg;
                else
                    close(dlg);
                    msg = sprintf('%s LLR coverage report does not exist!\n路径: %s', testMode, rptFile);
                    app.MessagesTextArea.Value = msg;
                    uialert(app.MainFigure, msg, '覆盖度报告不存在');
                    return;
                end
                
                % 关闭进度对话框
                close(dlg);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && isvalid(dlg)
                    close(dlg);
                end
                
                % 显示错误（基于第2条规则：明确报告错误）
                uialert(app.MainFigure, ...
                    sprintf('打开LLR覆盖度报告失败:\n%s', ME.message), ...
                    '错误');
                app.MessagesTextArea.Value = sprintf('打开LLR覆盖度报告错误: %s', ME.message);
            end
        end
        
        % =============== Model Management Callbacks ===============
        
        function NewModelButtonPushed(app, varargin)
            % 创建新模型
            % 基于规则第4调用ModelManagementService
            try
                app.startLogging();
                template = app.ModelTemplateDropDown.Value;
                app.MessagesTextArea.Value = ['正在创建新模型，使用模板：' template];
                drawnow;
                
                % 获取模型名称
                modelNameInput = inputdlg('Enter model name.', 'Model Name', [1 50]);
                if isempty(modelNameInput)
                    app.MessagesTextArea.Value = '用户取消了模型创建';
                    return;
                end
                modelName = char(modelNameInput{1});
                
                % 显示进度条（基于规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                    'Message', '正在创建新模型...', 'Indeterminate', 'on');
                
                % 确保ModelService已初始化（单例模式）
                if isempty(app.ModelService)
                    app.ModelService = ModelManagementService.getInstance();
                end
                
                % 根据模板选择调用ModelService创建模型
                if strcmp(template, 'Single-Instance')
                    result = app.ModelService.createModel(modelName, 'Nonreusable');
                else
                    result = app.ModelService.createModel(modelName, 'Reusable');
                end
                
                % 刷新设置页面（模型列表和树）- 不显示内部进度条，使用外部统一进度条
                app.refreshModelList(false); % 不显示内部进度条
                app.refreshModelTree(false); % 不显示内部进度条
                
                close(d);
                figure(app.MainFigure); % 确保主窗口在前台（规则第8条）
                
                app.callBackEnding(result.message);
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                % 使用字符串拼接避免转义字符问题（Windows路径中的反斜杠）
                msg = ['创建模型失败: ', ME.message];
                app.callBackEnding(msg);
                figure(app.MainFigure); 
            end
        end

        function ImportModelButtonPushed(app, varargin)
            % 导入模型功能 - 使用正确的逻辑（不覆盖项目sldd，而是合并数据）
            % 基于规则第4使用ModelManagementService进行基本验证
            try
                app.startLogging();
                template = app.ModelTemplateDropDown.Value;
                app.MessagesTextArea.Value = '正在导入模型...';
                drawnow;
                
                % 让用户选择要导入的模型文件
                [fileName, pathName] = uigetfile({'*.slx';'*.mdl'}, '选择要导入的Simulink模型');
                if fileName == 0
                    app.MessagesTextArea.Value = '用户取消了模型导入';
                    return;
                end
                
                % 显示进度条（基于规则第8条）
                d = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                    'Message', '正在导入模型...', 'Indeterminate', 'on');
                
                % 获取模型名称（不包含扩展名）
                [~, modelName, ~] = fileparts(fileName);
                fullSourcePath = fullfile(pathName, fileName);
                
                % 确保ModelService已初始化（单例模式）
                if isempty(app.ModelService)
                    app.ModelService = ModelManagementService.getInstance();
                end
                
                % 使用ModelService验证模型名称
                if ~app.ModelService.validateModelName(modelName)
                    close(d);
                    error([modelName, ' 不是有效的模型名称。模型名称必须是有效的MATLAB变量名。']);
                end
                
                % 检查模型是否已存在
                if app.ModelService.modelExists(modelName)
                    close(d);
                    error(['模型 ''', modelName, ''' 已经存在。']);
                end
                
                % 检测模型中的Model Reference并询问用户是否要导入
                d.Message = '正在检测模型引用...';
                referencedModels = app.detectModelReferences(fullSourcePath, pathName);
                
                modelsToImport = {fullSourcePath}; % 总是包含主模型
                
                if ~isempty(referencedModels)
                    close(d); % 暂时关闭进度条以显示对话框
                    
                    % 询问用户是否要导入被引用的模型
                    importRefs = app.promptForReferenceImport(referencedModels);
                    
                    if importRefs
                        % 用户选择导入引用模型
                        modelsToImport = [modelsToImport, referencedModels.filePaths];
                        app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, '将导入主模型及其引用的模型'];
                    else
                        app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, '仅导入主模型（不包含引用模型）'];
                    end
                    
                    % 重新显示进度条
                    d = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                        'Message', '正在导入模型...', 'Indeterminate', 'on');
                end
                
                % 调用导入模型函数（使用正确的逻辑）
                app.importModelsToProject(modelsToImport, modelName, template);
                
                msg = ['模型 ', modelName, ' 导入成功'];
                
                % 刷新设置页面（模型列表和树）- 不显示内部进度条，使用外部统一进度条
                app.refreshModelList(false); % 不显示内部进度条
                app.refreshModelTree(false); % 不显示内部进度条
                
                close(d);
                figure(app.MainFigure); % 确保主窗口在前台
                
                app.callBackEnding(msg);
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                msg = sprintf('导入模型失败: %s', ME.message);
                app.callBackEnding(msg);
                % figure(app.MainFigure) #tmpfix; % 确保主窗口在前台
            end
        end
        
        function importModelToProject(app, sourceModelPath, modelName, template)
            % 导入模型到项目中，采用正确的逻辑：
            % 1. 基于规则第4调用ProjectManagementService创建项目结构和标准sldd（2025-10-14重构）
            % 2. 复制源模型文件
            % 3. 合并源sldd的数据到项目sldd（保留项目配置集引用）
            
            % 确保ProjectManagementService已初始化（单例模式）
            if isempty(app.ProjectManagementService)
                app.ProjectManagementService = ProjectManagementService.getInstance('Environment', 'DEV');
            end
            
            % 步骤1: 首先基于模板创建项目标准的模型文件夹结构
            % 基于规则第4调用ProjectManagementService创建模型（这会创建正确的sldd文件，包含项目标准的配置集引用）
            if strcmp(template, 'Single-Instance')
                result = app.ProjectManagementService.createModel(modelName, 'Nonreusable');
                reuse = false;
            else
                result = app.ProjectManagementService.createModel(modelName);
                reuse = true;
            end
            
            % 检查创建结果
            if ~result.success
                error('创建模型文件夹失败: %s', result.message);
            end
            
            % 步骤2: 获取目标模型路径并替换模板模型
            targetModelPath = fullfile(ProjectPathManager.getDirPath('model', modelName), [modelName, '.slx']);
            
            % 关闭所有模型以避免冲突
            bdclose('all');
            
            % 直接复制源模型到目标位置（覆盖模板创建的空模型）
            copyfile(sourceModelPath, targetModelPath);
            
            % 步骤3: 合并源模型的sldd数据到项目sldd中（保留项目配置集）
            app.mergeSourceDataToProjectSLDD(sourceModelPath, modelName, reuse);
            
            app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, '模型导入完成，已保留项目配置集引用'];
        end
        
        function mergeSourceDataToProjectSLDD(app, sourceModelPath, targetModelName, reuse)
            % 将源模型的sldd数据合并到项目创建的sldd中，保留项目配置集引用
            try
                % 打开源模型检查其数据字典设置
                tempModel = load_system(sourceModelPath);
                dataDictPath = get_param(tempModel, 'DataDictionary');
                
                if ~isempty(dataDictPath)
                    % 构建路径
                    targetDDName = ProjectNamingManager.getNameStr('model data', targetModelName);
                    targetDDPath = fullfile(ProjectPathManager.getDirPath('model data', targetModelName), [targetDDName, '.sldd']);
                    
                    % 查找源数据字典文件
                    fullSourceDDPath = app.findSourceDataDictionary(sourceModelPath, dataDictPath);
                    
                    if ~isempty(fullSourceDDPath) && exist(targetDDPath, 'file')
                        % 合并数据字典内容
                        app.mergeDataDictionaryContent(fullSourceDDPath, targetDDPath, reuse);
                        app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, '已合并源数据字典数据，保留项目配置集'];
                    else
                        app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, '源模型未使用数据字典或文件不存在'];
                    end
                end
                
                % 关闭临时加载的模型
                close_system(tempModel, 0);
                
            catch ME
                warning('ImportModel:MergeDataWarning', '合并sldd数据时出现警告: %s', ME.message);
            end
        end
        
        function fullSourceDDPath = findSourceDataDictionary(app, sourceModelPath, dataDictPath)
            % 查找源数据字典文件的完整路径
            fullSourceDDPath = '';
            
            if exist(dataDictPath, 'file')
                fullSourceDDPath = dataDictPath;
            else
                % 尝试相对于源模型路径查找
                [sourceModelDir, ~, ~] = fileparts(sourceModelPath);
                [~, sourceDDName, sourceDDExt] = fileparts(dataDictPath);
                potentialDDPath = fullfile(sourceModelDir, [sourceDDName, sourceDDExt]);
                if exist(potentialDDPath, 'file')
                    fullSourceDDPath = potentialDDPath;
                end
            end
        end
        
        function mergeDataDictionaryContent(app, sourceDDPath, targetDDPath, reuse)
            % 合并源数据字典的内容到目标数据字典，保留目标字典的配置集引用
            try
                % 打开两个数据字典
                sourceDD = Simulink.data.dictionary.open(sourceDDPath);
                targetDD = Simulink.data.dictionary.open(targetDDPath);
                
                % 获取设计数据部分
                sourceDDataSect = getSection(sourceDD, 'Design Data');
                targetDDataSect = getSection(targetDD, 'Design Data');
                
                % 复制设计数据
                sourceEntries = find(sourceDDataSect);
                for i = 1:length(sourceEntries)
                    entryName = sourceEntries(i).Name;
                    entryValue = sourceEntries(i).Value;
                    
                    % 检查目标数据字典中是否已存在该条目
                    if ~exist(targetDDataSect, entryName)
                        % 添加新条目
                        addEntry(targetDDataSect, entryName, entryValue);
                        app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, '添加数据变量: ', entryName];
                    else
                        % 更新现有条目
                        targetEntry = getEntry(targetDDataSect, entryName);
                        targetEntry.Value = entryValue;
                        app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, '更新数据变量: ', entryName];
                    end
                end
                
                % 确保目标数据字典仍然包含正确的项目配置集数据源
                targetDataSources = getDataSources(targetDD);
                correctDataSource = '';
                if reuse
                    correctDataSource = 'csMultiInstance.sldd';
                else
                    correctDataSource = 'csSingleInstance.sldd';
                end
                
                % 检查是否已有正确的数据源
                hasCorrectDataSource = false;
                for i = 1:length(targetDataSources)
                    if strcmp(targetDataSources{i}, correctDataSource)
                        hasCorrectDataSource = true;
                        break;
                    end
                end
                
                % 如果没有正确的数据源，添加它
                if ~hasCorrectDataSource
                    addDataSource(targetDD, correctDataSource);
                    app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, '确保配置集数据源: ', correctDataSource];
                end
                
                % 保存更改并关闭
                saveChanges(targetDD);
                close(sourceDD);
                close(targetDD);
                
            catch ME
                % 确保关闭数据字典
                try
                    close(sourceDD);
                catch
                end
                try
                    close(targetDD);
                catch
                end
                rethrow(ME);
            end
        end
        
        function referencedModels = detectModelReferences(app, modelPath, searchPath)
            % 检测模型中的Model Reference块并查找对应的模型文件
            referencedModels = struct('modelNames', {{}}, 'filePaths', {{}}, 'notFound', {{}});
            
            try
                % 临时加载模型
                tempModel = load_system(modelPath);
                
                % 查找所有ModelReference块
                modelRefBlocks = find_system(tempModel, 'BlockType', 'ModelReference');
                
                if ~isempty(modelRefBlocks)
                    app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, ...
                        sprintf('发现 %d 个模型引用块', length(modelRefBlocks))];
                    
                    for i = 1:length(modelRefBlocks)
                        try
                            % 获取引用的模型名称
                            refModelName = get_param(modelRefBlocks{i}, 'ModelName');
                            
                            if ~isempty(refModelName) && ~strcmp(refModelName, 'Model')
                                % 查找模型文件
                                refModelPath = app.findReferencedModelFile(refModelName, searchPath);
                                
                                if ~isempty(refModelPath)
                                    referencedModels.modelNames{end+1} = refModelName;
                                    referencedModels.filePaths{end+1} = refModelPath;
                                    app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, ...
                                        sprintf('找到引用模型: %s', refModelName)];
                                else
                                    referencedModels.notFound{end+1} = refModelName;
                                    app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, ...
                                        sprintf('警告: 未找到引用模型文件: %s', refModelName)];
                                end
                            end
                        catch
                            % 忽略单个块的错误，继续处理其他块
                            continue;
                        end
                    end
                end
                
                % 关闭临时模型
                close_system(tempModel, 0);
                
            catch ME
                warning('ImportModel:DetectReferencesWarning', '检测模型引用时出现警告: %s', ME.message);
            end
        end
        
        function modelPath = findReferencedModelFile(app, modelName, searchPath)
            % 在指定路径中查找引用的模型文件
            modelPath = '';
            
            % 可能的文件扩展名
            extensions = {'.slx', '.mdl'};
            
            for ext = extensions
                % 在源模型同一目录查找
                potentialPath = fullfile(searchPath, [modelName, ext{1}]);
                if exist(potentialPath, 'file')
                    modelPath = potentialPath;
                    return;
                end
                
                % 在MATLAB路径中查找
                foundPath = which([modelName, ext{1}]);
                if ~isempty(foundPath) && exist(foundPath, 'file')
                    modelPath = foundPath;
                    return;
                end
            end
        end
        
        function importRefs = promptForReferenceImport(app, referencedModels)
            % 询问用户是否要导入被引用的模型
            importRefs = false;
            
            if isempty(referencedModels.modelNames)
                return;
            end
            
            % 构建提示信息
            foundModels = referencedModels.modelNames;
            notFoundModels = referencedModels.notFound;
            
            message = sprintf('检测到 %d 个模型引用:\n\n', length(foundModels));
            
            if ~isempty(foundModels)
                message = [message, '可以导入的模型:\n'];
                for i = 1:length(foundModels)
                    message = [message, sprintf('• %s\n', foundModels{i})];
                end
                message = [message, newline];
            end
            
            if ~isempty(notFoundModels)
                message = [message, '未找到的模型文件:\n'];
                for i = 1:length(notFoundModels)
                    message = [message, sprintf('• %s (文件未找到)\n', notFoundModels{i})];
                end
                message = [message, newline];
            end
            
            message = [message, '是否要一并导入找到的引用模型？\n\n'];
            message = [message, '选择"是"：导入主模型及其所有找到的引用模型\n'];
            message = [message, '选择"否"：仅导入主模型'];
            
            % 显示对话框
            choice = questdlg(message, '导入模型引用', '是', '否', '取消', '是');
            
            switch choice
                case '是'
                    importRefs = true;
                case '否'
                    importRefs = false;
                case '取消'
                    error('用户取消了导入操作');
                otherwise
                    importRefs = false;
            end
        end
        
        function importModelsToProject(app, modelPaths, primaryModelName, template)
            % 批量导入多个模型到项目中
            importedModels = {};
            
            try
                for i = 1:length(modelPaths)
                    modelPath = modelPaths{i};
                    [~, currentModelName, ~] = fileparts(modelPath);
                    
                    % 检查模型是否已经导入过（避免重复导入）
                    if ismember(currentModelName, importedModels)
                        app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, ...
                            sprintf('跳过已导入的模型: %s', currentModelName)];
                        continue;
                    end
                    
                    % 检查模型是否已存在于项目中
                    if exist(currentModelName, 'file')
                        app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, ...
                            sprintf('跳过已存在的模型: %s', currentModelName)];
                        continue;
                    end
                    
                    app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, ...
                        sprintf('正在导入模型: %s', currentModelName)];
                    
                    % 导入单个模型
                    if i == 1
                        % 第一个是主模型，使用用户选择的模板
                        app.importModelToProject(modelPath, currentModelName, template);
                    else
                        % 引用的模型默认使用Single-Instance模板（可以根据需要调整）
                        defaultTemplate = 'Single-Instance';
                        app.importModelToProject(modelPath, currentModelName, defaultTemplate);
                    end
                    
                    importedModels{end+1} = currentModelName;
                    app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, ...
                        sprintf('✓ 模型 %s 导入成功', currentModelName)];
                end
                
                app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, ...
                    sprintf('批量导入完成，共导入 %d 个模型', length(importedModels))];
                
            catch ME
                app.MessagesTextArea.Value = [app.MessagesTextArea.Value, newline, ...
                    sprintf('批量导入过程中出现错误: %s', ME.message)];
                rethrow(ME);
            end
        end
        
        function initializeConfigTree(app)
            % 初始化项目配置文件树形结构 - 调用独立业务逻辑
            
            % 清空树
            delete(app.ConfigTree.Children);
            
            try
                % 获取项目配置目录路径（调用业务逻辑）
                configPath = ProjectConfigManager('getConfigPath');
                
                if isempty(configPath)
                    % 如果配置目录不存在，创建提示节点
                    noConfigNode = uitreenode(app.ConfigTree);
                    noConfigNode.Text = '❌ 项目配置目录不存在';
                    noConfigNode.UserData = struct('type', 'error', 'path', '');
                    return;
                end
                
                % 获取分类数据并创建节点
                categoryData = ProjectConfigManager('getCategoryData', configPath);
                app.createConfigCategoryNodesFromData(configPath, categoryData);
                
            catch ME
                % 错误处理
                errorNode = uitreenode(app.ConfigTree);
                errorNode.Text = ['❌ 配置加载失败: ' ME.message];
                errorNode.UserData = struct('type', 'error', 'path', '');
            end
        end
        
        function createConfigCategoryNodesFromData(app, configPath, categoryData)
            % 基于业务逻辑返回的数据创建配置分类节点
            
            if isempty(categoryData)
                return;
            end
            
            % 存储所有创建的节点，用于最后展开
            createdNodes = {};
            
            % 创建各个分类节点
            categoryNames = fieldnames(categoryData);
            for i = 1:length(categoryNames)
                categoryName = categoryNames{i};
                categoryInfo = categoryData.(categoryName);
                
                % 创建分类节点
                categoryNode = uitreenode(app.ConfigTree);
                categoryNode.Text = categoryInfo.name;
                categoryNode.UserData = struct('type', 'category', 'path', '', 'description', categoryInfo.description);
                
                % 添加该分类下的文件
                if strcmp(categoryName, 'templates')
                    % 模板文件需要特殊处理（递归结构）
                    app.addTemplateFilesFromData(categoryNode, categoryInfo);
                elseif strcmp(categoryName, 'checks') && isfield(categoryInfo, 'path')
                    % 检查规则文件从指定目录读取
                    app.addFilesFromDirectory(categoryNode, categoryInfo.path, 'checks');
                else
                    % 普通文件列表
                    app.addFilesFromList(categoryNode, configPath, categoryInfo.files, categoryName);
                end
                
                createdNodes{end+1} = categoryNode;
            end
            
            % 展开所有分类节点
            for i = 1:length(createdNodes)
                try
                    expand(createdNodes{i}, 'all');
                catch
                    % 忽略展开失败的错误
                end
            end
        end
        
        function addFilesFromList(app, parentNode, basePath, fileList, category)
            % 从文件列表添加文件节点
            for i = 1:length(fileList)
                filePath = fullfile(basePath, fileList{i});
                if exist(filePath, 'file')
                    fileNode = uitreenode(parentNode);
                    [~, name, ext] = fileparts(fileList{i});
                    fileNode.Text = ['📄 ' name ext];
                    fileNode.UserData = struct('type', 'file', 'path', filePath, 'category', category);
                end
            end
        end
        
        function addFilesFromDirectory(app, parentNode, dirPath, category)
            % 从目录添加文件节点
            if ~exist(dirPath, 'dir')
                return;
            end
            
            files = dir(fullfile(dirPath, '*'));
            files = files(~[files.isdir]); % 只要文件
            for i = 1:length(files)
                filePath = fullfile(dirPath, files(i).name);
                fileNode = uitreenode(parentNode);
                [~, name, ext] = fileparts(files(i).name);
                fileNode.Text = ['📄 ' name ext];
                fileNode.UserData = struct('type', 'file', 'path', filePath, 'category', category);
            end
        end
        
        function addTemplateFilesFromData(app, parentNode, templateInfo)
            % 从模板数据添加模板文件节点
            if ~isfield(templateInfo, 'fileTree') || isempty(templateInfo.fileTree)
                return;
            end
            
            app.addFileTreeNodes(parentNode, templateInfo.fileTree.children, 'templates');
        end
        
        function addFileTreeNodes(app, parentNode, children, category)
            % 递归添加文件树节点
            for i = 1:length(children)
                child = children{i};
                
                if child.isDir
                    % 文件夹节点
                    folderNode = uitreenode(parentNode);
                    folderNode.Text = ['📁 ' child.name];
                    folderNode.UserData = struct('type', 'folder', 'path', child.path, 'category', category);
                    
                    % 递归添加子节点
                    if ~isempty(child.children)
                        app.addFileTreeNodes(folderNode, child.children, category);
                    end
                else
                    % 文件节点
                    fileNode = uitreenode(parentNode);
                    [~, name, ext] = fileparts(child.name);
                    fileNode.Text = ['📄 ' name ext];
                    fileNode.UserData = struct('type', 'file', 'path', child.path, 'category', category);
                end
            end
        end
        
        % =============== Model Dashboard Callbacks ===============
        
        function RefreshModelDashboardButtonPushed(app, varargin)
            % 刷新模型仪表盘 - 占位函数
            try
                app.MessagesTextArea.Value = '刷新模型仪表盘 - 功能开发中...';
                app.callBackEnding('刷新模型仪表盘（占位功能）');
                % figure(app.MainFigure) #tmpfix;
            catch ME
                uialert(app.MainFigure, sprintf('刷新模型仪表盘失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function OpenModelDashboardButtonPushed(app, varargin)
            % 打开模型仪表盘 - 占位函数
            try
                app.MessagesTextArea.Value = '打开模型仪表盘 - 功能开发中...';
                app.callBackEnding('打开模型仪表盘（占位功能）');
                % figure(app.MainFigure) #tmpfix;
            catch ME
                uialert(app.MainFigure, sprintf('打开模型仪表盘失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        
        function RefreshConfigButtonPushed(app, varargin)
            % 刷新配置文件按钮回调
            try
                app.initializeConfigTree();
                app.callBackEnding('配置文件列表已刷新');
            catch ME
                app.callBackEnding(['刷新配置文件失败: ' ME.message]);
                uialert(app.MainFigure, ME.message, '刷新配置文件错误', 'Icon', 'warning');
            end
            
            % 确保主窗口回到前台（规则第8条）
            % figure(app.MainFigure) #tmpfix;
        end
        
        function OpenConfigFileButtonPushed(app, varargin)
            % 打开选中配置文件按钮回调 - 调用独立业务逻辑
            try
                % 检查是否有选中的节点
                if isempty(app.ConfigTree.SelectedNodes)
                    uialert(app.MainFigure, '请先选择一个配置文件', '提示', 'Icon', 'info');
                    return;
                end
                
                selectedNode = app.ConfigTree.SelectedNodes(1);
                userData = selectedNode.UserData;
                
                % 只处理文件类型的节点
                if strcmp(userData.type, 'file')
                    filePath = userData.path;
                    
                    % 获取文件扩展名
                    [~, ~, ext] = fileparts(filePath);
                    
                    % 判断是否为CSV或Excel文件（不区分大小写）
                    isCsvOrExcel = any(strcmpi(ext, {'.csv', '.xlsx', '.xls', '.xlsm', '.xlsb'}));
                    
                    % 显示进度条（规则第8条）
                    dlg = [];
                    if isCsvOrExcel
                        dlg = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                            'Message', '正在使用系统默认工具打开文件...', 'Indeterminate', 'on');
                    else
                        dlg = uiprogressdlg(app.MainFigure, 'Title', '请稍候', ...
                            'Message', '正在打开配置文件...', 'Indeterminate', 'on');
                    end
                    
                    if isCsvOrExcel
                        % CSV和Excel文件使用系统默认工具打开
                        [success, errorMsg] = configManager.openFileWithSystem(filePath);
                        if success
                            app.callBackEnding(['已使用系统默认工具打开文件: ' filePath]);
                        else
                            error(errorMsg);
                        end
                    else
                        % 其他文件调用业务逻辑打开（在MATLAB编辑器中打开）
                        success = ProjectConfigManager('openFile', filePath);
                        if success
                            app.callBackEnding(['已打开配置文件: ' filePath]);
                        end
                    end
                    
                    % 关闭进度对话框
                    if ~isempty(dlg) && isvalid(dlg)
                        close(dlg);
                    end
                else
                    uialert(app.MainFigure, '请选择一个文件节点', '提示', 'Icon', 'info');
                end
                
            catch ME
                % 如果进度对话框还在，关闭它
                if exist('dlg', 'var') && ~isempty(dlg) && isvalid(dlg)
                    close(dlg);
                end
                
                app.callBackEnding(['打开配置文件失败: ' ME.message]);
                uialert(app.MainFigure, ME.message, '打开文件错误', 'Icon', 'warning');
            end
            
        end
        
        function ConfigTreeSelectionChanged(app, src, event)
            % 配置文件树选择变化回调 - 调用独立业务逻辑
            try
                % 清空信息显示
                app.ConfigFilePathLabel.Text = '请选择配置文件查看详细信息';
                app.ConfigFileInfoLabel.Text = '';
                
                % 检查是否有选中的节点
                if isempty(app.ConfigTree.SelectedNodes)
                    return;
                end
                
                selectedNode = app.ConfigTree.SelectedNodes(1);
                userData = selectedNode.UserData;
                
                if strcmp(userData.type, 'file')
                    % 获取并显示文件信息（调用业务逻辑）
                    filePath = userData.path;
                    app.ConfigFilePathLabel.Text = ['文件路径: ' filePath];
                    
                    fileInfo = ProjectConfigManager('getFileInfo', filePath);
                    if fileInfo.exists
                        infoText = sprintf('文件名: %s\n大小: %s\n修改时间: %s\n类型: %s\n分类: %s', ...
                            fileInfo.name, ...
                            fileInfo.sizeFormatted, ...
                            fileInfo.dateModified, ...
                            fileInfo.type, ...
                            fileInfo.category);
                        
                        app.ConfigFileInfoLabel.Text = infoText;
                    else
                        app.ConfigFileInfoLabel.Text = ['❌ ' fileInfo.error];
                    end
                    
                elseif strcmp(userData.type, 'folder')
                    % 显示文件夹信息
                    folderPath = userData.path;
                    app.ConfigFilePathLabel.Text = ['文件夹路径: ' folderPath];
                    
                    if exist(folderPath, 'dir')
                        items = dir(folderPath);
                        fileCount = sum(~[items.isdir]);
                        folderCount = sum([items.isdir]) - 2; % 减去. 和 ..
                        
                        infoText = sprintf('文件夹内容:\n文件数量: %d\n子文件夹数量: %d\n分类: %s', ...
                            fileCount, folderCount, userData.category);
                        
                        app.ConfigFileInfoLabel.Text = infoText;
                    else
                        app.ConfigFileInfoLabel.Text = '❌ 文件夹不存在';
                    end
                    
                elseif strcmp(userData.type, 'category')
                    % 显示分类信息
                    app.ConfigFilePathLabel.Text = ['配置分类: ' selectedNode.Text];
                    
                    % 统计该分类下的文件数量
                    childCount = length(selectedNode.Children);
                    infoText = sprintf('该分类包含 %d 个配置项', childCount);
                    
                    % 如果有分类描述，也显示出来
                    if isfield(userData, 'description') && ~isempty(userData.description)
                        infoText = [infoText, sprintf('\n描述: %s', userData.description)];
                    end
                    
                    app.ConfigFileInfoLabel.Text = infoText;
                end
                
            catch ME
                app.ConfigFilePathLabel.Text = '获取文件信息时出错';
                app.ConfigFileInfoLabel.Text = ME.message;
            end
        end
        
        function initializeUserName(app)
            % 初始化用户名 - 从getUserAccount获取系统用户名
            try
                % 调用getUserAccount获取用户名
                systemUserName = getUserAccount();
                
                % 设置当前用户名属性
                app.userName = string(systemUserName);
                
                % 设置编辑框的值和标签
                app.UserNameEditField.Value = systemUserName;
                app.UserNameLabel.Text = sprintf('当前用户: %s', systemUserName);
                
            catch ME
                % 如果获取失败，使用默认值
                app.userName = "Unknown User";
                app.UserNameEditField.Value = "Unknown User";
                app.UserNameLabel.Text = '当前用户: 获取失败';
                
                fprintf('⚠️ 初始化用户名失败: %s\n', ME.message);
            end
        end
        
        function SaveUserNameButtonPushed(app, varargin)
            % 保存用户名按钮回调 - 会话级别保存（不写入文件）
            try
                % 获取编辑框中的新用户名
                newUserName = strtrim(app.UserNameEditField.Value);
                
                % 验证用户名不为空
                if isempty(newUserName)
                    uialert(app.MainFigure, '用户名不能为空！', '输入错误', 'Icon', 'warning');
                    return;
                end
                
                % 更新当前用户名属性
                app.userName = string(newUserName);
                
                % 更新标签显示
                app.UserNameLabel.Text = sprintf('当前用户: %s', newUserName);
                
                % 显示成功消息
                uialert(app.MainFigure, sprintf('用户名已更新为: %s\n\n注意：此更改仅在当前会话中有效', newUserName), ...
                    '用户名已更新', 'Icon', 'success');
                
                % 设置消息和日志
                app.callBackEnding(sprintf('用户名已更新为: %s', newUserName));
                fprintf('✅ 用户名已更新: %s\n', newUserName);
                
            catch ME
                errorMsg = sprintf('保存用户名失败: %s', ME.message);
                uialert(app.MainFigure, errorMsg, '保存用户名错误', 'Icon', 'error');
                app.callBackEnding(errorMsg);
                fprintf('❌ %s\n', errorMsg);
            end
            
            % 确保主窗口回到前台（规则第8条）
            % figure(app.MainFigure) #tmpfix;
        end
        function initializeVersionInfo(app)
            % 初始化版本信息 - 从版本函数获取并显示
            try
                % 获取工具版本信息
                try
                    toolVerInfo = getToolsVersion();
                    app.ToolVersionEditField.Value = toolVerInfo.version;
                    fprintf('✅ 工具版本加载成功: %s (构建日期: %s, Commit: %s)\n', ...
                        toolVerInfo.version, toolVerInfo.buildDate, toolVerInfo.commitHash);
                catch ME_tool
                    app.ToolVersionEditField.Value = '未知';
                    fprintf('⚠️ 工具版本加载失败: %s\n', ME_tool.message);
                end
                
                % 获取项目配置版本信息
                try
                    configVerInfo = getProjectConfigVersion();
                    app.ProjectConfigVersionEditField.Value = configVerInfo.version;
                    fprintf('✅ 项目配置版本加载成功: %s (构建日期: %s, Commit: %s)\n', ...
                        configVerInfo.version, configVerInfo.buildDate, configVerInfo.commitHash);
                catch ME_config
                    app.ProjectConfigVersionEditField.Value = '未知';
                    fprintf('⚠️ 项目配置版本加载失败: %s\n', ME_config.message);
                end
                
                % 软件版本（保持为空或未来用于目标软件版本）
                app.SoftwareVersionEditField.Value = '';
                
                fprintf('✅ 版本信息初始化完成\n');
                
            catch ME
                fprintf('⚠️ 初始化版本信息失败: %s\n', ME.message);
            end
        end
        
        function currentUserName = getCurrentUserName(app)
            % 获取当前用户名的便捷方法
            % 返回值: 当前用户名字符串
            % 用法: userName = app.getCurrentUserName();
            if isempty(app.userName)
                % 如果用户名为空，重新初始化
                app.initializeUserName();
            end
            currentUserName = char(app.userName);
        end
        
        function initializeAdminPermissions(app)
            % 初始化管理员权限并控制相关按钮可见性
            % 设计原则：即使ALM服务不可用，也不阻止app启动
            try
                % 检查审批按钮是否存在（必须在ALM Tab创建后调用此函数）
                if ~isprop(app, 'ApprovePRButton') || isempty(app.ApprovePRButton) || ...
                   ~isprop(app, 'ApproveCRButton') || isempty(app.ApproveCRButton)
                    fprintf('⚠️ 审批按钮未创建，跳过权限初始化\n');
                    return;
                end
                
                % 默认隐藏审批按钮（安全优先原则）
                app.ApprovePRButton.Visible = 'off';
                app.ApproveCRButton.Visible = 'off';
                
                % 尝试初始化ALM服务并查询权限
                try
                    % 确保ALM服务已初始化
                    % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                    if isempty(app.ALMService)
                        app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                    end
                    
                    % 查询当前用户角色（设置短超时，避免长时间等待）
                    [success, userRole] = app.ALMService.getUserRole();
                    
                    if success && isfield(userRole, 'role')
                        % 检查是否为admin用户
                        isAdmin = strcmp(userRole.role, 'admin');
                        
                        % 控制审批按钮的可见性
                        if isAdmin
                            app.ApprovePRButton.Visible = 'on';
                            app.ApproveCRButton.Visible = 'on';
                            fprintf('✅ Admin权限检测成功：用户 %s 具有admin权限，审批按钮已启用\n', app.getCurrentUserName());
                        else
                            fprintf('ℹ️ 权限检测：用户 %s 角色为 %s，审批按钮已隐藏\n', app.getCurrentUserName(), userRole.role);
                        end
                        
                    else
                        fprintf('⚠️ 无法获取用户权限信息，审批按钮保持隐藏\n');
                    end
                    
                catch networkME
                    % 网络连接失败不应阻止app启动
                    fprintf('⚠️ ALM服务连接失败（%s），审批按钮已隐藏。app正常启动\n', networkME.message);
                end
                
            catch ME
                % 任何其他异常都不应阻止app启动
                fprintf('⚠️ 权限初始化异常: %s。app将继续正常启动\n', ME.message);
                
                % 尝试安全地隐藏审批按钮
                try
                    if isprop(app, 'ApprovePRButton') && ~isempty(app.ApprovePRButton)
                        app.ApprovePRButton.Visible = 'off';
                    end
                    if isprop(app, 'ApproveCRButton') && ~isempty(app.ApproveCRButton)
                        app.ApproveCRButton.Visible = 'off';
                    end
                catch
                    % 即使设置按钮可见性失败，也继续启动
                    fprintf('⚠️ 无法设置按钮可见性，但app继续启动\n');
                end
            end
        end
        
        function isAdmin = isCurrentUserAdmin(app)
            % 检查当前用户是否为admin
            % 返回值：true/false
            try
                % 确保ALM服务已初始化
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                
                % 查询当前用户角色
                [success, userRole] = app.ALMService.getUserRole();
                
                if success && isfield(userRole, 'role')
                    isAdmin = strcmp(userRole.role, 'admin');
                else
                    isAdmin = false;
                end
                
            catch
                % 发生异常时默认为非admin
                isAdmin = false;
            end
        end
        
        
        function CreateModelChecklistButtonPushed(app, varargin)
            % 新建模型检查清单
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                currentModel = app.CurrentModelDropDown.Value;
                % 基于规则第4通过Service Layer调用，而非直接调用底层工具函数
                result = app.ModelStaticAnalysisService.createChecklist(currentModel, 'model review checklist');
                app.MessagesTextArea.Value = result.message;
                app.updateStatusLights();
                app.callBackEnding('创建模型检查清单');
                
            catch ME
                uialert(app.MainFigure, sprintf('创建模型检查清单失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function OpenModelChecklistButtonPushed(app, varargin)
            % 打开模型检查清单
            try
                if ~app.validateCurrentModel()
                    return;
                end
                
                currentModel = app.CurrentModelDropDown.Value;
                % 基于规则第4通过Service Layer调用，而非直接调用底层工具函数
                result = app.ModelStaticAnalysisService.createChecklist(currentModel, 'model review checklist', true);
                app.MessagesTextArea.Value = result.message;
                app.callBackEnding('打开模型评审清单');
                
            catch ME
                uialert(app.MainFigure, sprintf('打开模型检查清单失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function onSubmitPRButtonPushed(app, varargin)
            % 提交PR按钮回调函数 - 弹出PR提交表单对话框
            try
                % 创建并显示PR提交表单对话框
                app.createPRSubmissionDialog();
                
            catch ME
                uialert(app.MainFigure, sprintf('打开PR提交表单失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function onSubmitCRButtonPushed(app, varargin)
            % 提交CR按钮回调函数 - 弹出CR提交表单对话框
            try
                % 创建并显示CR提交表单对话框
                app.createCRSubmissionDialog();
                
            catch ME
                uialert(app.MainFigure, sprintf('打开CR提交表单失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function onRefreshPRCRButtonPushed(app, varargin)
            % 刷新PR/CR按钮回调函数 - 调用核心刷新逻辑
            % 显示进度条和详细结果反馈
            app.refreshPRCRDataCore(true);
        end
        
        function onOpenSelectedPRButtonPushed(app, varargin)
            % 打开选中PR按钮回调函数 - 显示PR的详情对话框
            try
                % 防重复点击保护
                app.OpenSelectedPRButton.Enable = 'off';
                
                % 检查PR表格是否有选中的行
                if isempty(app.PRTable.Data) || isempty(app.PRTable.Selection)
                    uialert(app.MainFigure, '请先选择一个PR！', '提示', 'Icon', 'info');
                    app.OpenSelectedPRButton.Enable = 'on';
                    return;
                end
                
                    % 打开PR详情
                selectedRow = app.PRTable.Selection(1);  % 取第一个选中的行
                    if size(app.PRTable.Data, 1) >= selectedRow
                        prIdRaw = app.PRTable.Data{selectedRow, 1};  % 第一列是PR ID
                        
                        % 数据清理和验证
                        if isnumeric(prIdRaw)
                            prId = prIdRaw;
                        elseif ischar(prIdRaw) || isstring(prIdRaw)
                            prId = str2double(string(prIdRaw));
                        else
                            error('PR ID数据格式错误: %s', class(prIdRaw));
                        end
                        
                        % 验证ID有效性
                        if isnan(prId) || prId <= 0
                            error('无效的PR ID: %s', string(prIdRaw));
                        end
                        
                        fprintf('🔍 用户点击打开PR详情 - ID: %d\n', prId);
                        app.openPRDetail(prId);
                    else
                        uialert(app.MainFigure, '选中的PR不存在！', '错误', 'Icon', 'error');
                    end
                
                % 恢复按钮状态
                app.OpenSelectedPRButton.Enable = 'on';
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 恢复按钮状态
                app.OpenSelectedPRButton.Enable = 'on';
                uialert(app.MainFigure, sprintf('打开PR详情失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function onOpenSelectedCRButtonPushed(app, varargin)
            % 打开选中CR按钮回调函数 - 显示CR的详情对话框
            try
                % 防重复点击保护
                app.OpenSelectedCRButton.Enable = 'off';
                
                % 检查CR表格是否有选中的行
                if isempty(app.CRTable.Data) || isempty(app.CRTable.Selection)
                    uialert(app.MainFigure, '请先选择一个CR！', '提示', 'Icon', 'info');
                    app.OpenSelectedCRButton.Enable = 'on';
                    return;
                end
                
                    % 打开CR详情
                selectedRow = app.CRTable.Selection(1);  % 取第一个选中的行
                    if size(app.CRTable.Data, 1) >= selectedRow
                        crIdRaw = app.CRTable.Data{selectedRow, 1};  % 第一列是CR ID
                        
                        % 数据清理和验证
                        if isnumeric(crIdRaw)
                            crId = crIdRaw;
                        elseif ischar(crIdRaw) || isstring(crIdRaw)
                            crId = str2double(string(crIdRaw));
                        else
                            error('CR ID数据格式错误: %s', class(crIdRaw));
                        end
                        
                        % 验证ID有效性
                        if isnan(crId) || crId <= 0
                            error('无效的CR ID: %s', string(crIdRaw));
                        end
                        
                        fprintf('🔍 用户点击打开CR详情 - ID: %d\n', crId);
                        app.openCRDetail(crId);
                    else
                        uialert(app.MainFigure, '选中的CR不存在！', '错误', 'Icon', 'error');
                end
                
                % 恢复按钮状态
                app.OpenSelectedCRButton.Enable = 'on';
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                % 恢复按钮状态
                app.OpenSelectedCRButton.Enable = 'on';
                uialert(app.MainFigure, sprintf('打开CR详情失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function onApprovePRButtonPushed(app, varargin)
            % 审批PR按钮回调函数 - 仅admin用户可见
            try
                % 检查是否有选中的PR
                if isempty(app.PRTable.Data) || isempty(app.PRTable.Selection)
                    uialert(app.MainFigure, '请先选择一个PR进行审批！', '提示', 'Icon', 'info');
                    return;
                end
                
                selectedRow = app.PRTable.Selection(1);
                if size(app.PRTable.Data, 1) >= selectedRow
                    prId = app.PRTable.Data{selectedRow, 1};  % 第一列是数据库ID
                    prTitle = app.PRTable.Data{selectedRow, 3};  % 第三列是标题
                    app.openPRApprovalDialog(prId, prTitle);
                else
                    uialert(app.MainFigure, '选中的PR不存在！', '错误', 'Icon', 'error');
                end
                
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                uialert(app.MainFigure, sprintf('打开PR审批失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function onApproveCRButtonPushed(app, varargin)
            % 审批CR按钮回调函数 - 仅admin用户可见
            try
                % 检查是否有选中的CR
                if isempty(app.CRTable.Data) || isempty(app.CRTable.Selection)
                    uialert(app.MainFigure, '请先选择一个CR进行审批！', '提示', 'Icon', 'info');
                    return;
                end
                
                selectedRow = app.CRTable.Selection(1);
                if size(app.CRTable.Data, 1) >= selectedRow
                    crId = app.CRTable.Data{selectedRow, 1};  % 第一列是数据库ID
                    crTitle = app.CRTable.Data{selectedRow, 3};  % 第三列是标题
                    app.openCRApprovalDialog(crId, crTitle);
                else
                    uialert(app.MainFigure, '选中的CR不存在！', '错误', 'Icon', 'error');
                end
                
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                uialert(app.MainFigure, sprintf('打开CR审批失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function openPRDetail(app, prId)
            % 打开PR详情查看对话框
            % 输入参数验证和单次调用保护
            
            try
                % 输入参数验证
                if ~isnumeric(prId) || length(prId) ~= 1 || isnan(prId) || prId <= 0
                    error('无效的PR ID参数: %s (类型: %s)', string(prId), class(prId));
                end
                
                % 转换为整数
                prId = double(prId);
                fprintf('📋 开始查询PR详情 - ID: %d\n', prId);
                
                % 显示进度条
                d = uiprogressdlg(app.MainFigure, 'Title', 'PR详情', ...
                    'Message', sprintf('正在获取PR %d 的详细信息...', prId), 'Indeterminate', 'on');
                
                % 确保ALM服务已初始化
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                
                % 获取PR详细信息（只调用一次）
                [success, prDetail] = app.ALMService.getPRDetail(prId);
                
                close(d);
                
                if success && ~isempty(prDetail)
                    % 创建详情对话框
                    fprintf('✅ PR详情获取成功，准备显示对话框\n');
                    app.createPRDetailDialog(prDetail);
                else
                    uialert(app.MainFigure, sprintf('无法获取PR %d 的详细信息', prId), '错误', 'Icon', 'error');
                end
                
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                fprintf('❌ PR详情查询失败: %s\n', ME.message);
                uialert(app.MainFigure, sprintf('打开PR详情失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function openCRDetail(app, crId)
            % 打开CR详情查看对话框
            % 输入参数验证和单次调用保护
            
            try
                % 输入参数验证
                if ~isnumeric(crId) || length(crId) ~= 1 || isnan(crId) || crId <= 0
                    error('无效的CR ID参数: %s (类型: %s)', string(crId), class(crId));
                end
                
                % 转换为整数
                crId = double(crId);
                fprintf('📋 开始查询CR详情 - ID: %d\n', crId);
                
                % 显示进度条
                d = uiprogressdlg(app.MainFigure, 'Title', 'CR详情', ...
                    'Message', sprintf('正在获取CR %d 的详细信息...', crId), 'Indeterminate', 'on');
                
                % 确保ALM服务已初始化
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                
                % 获取CR详细信息（只调用一次）
                [success, crDetail] = app.ALMService.getCRDetail(crId);
                
                close(d);
                
                if success && ~isempty(crDetail)
                    % 创建详情对话框
                    fprintf('✅ CR详情获取成功，准备显示对话框\n');
                    app.createCRDetailDialog(crDetail);
                else
                    uialert(app.MainFigure, sprintf('无法获取CR %d 的详细信息', crId), '错误', 'Icon', 'error');
                end
                
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                fprintf('❌ CR详情查询失败: %s\n', ME.message);
                uialert(app.MainFigure, sprintf('打开CR详情失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function openPRApprovalDialog(app, prId, prTitle)
            % 打开PR审批对话框
            try
                % 创建审批对话框
                app.createPRApprovalDialog(prId, prTitle);
                
            catch ME
                uialert(app.MainFigure, sprintf('打开PR审批对话框失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function openCRApprovalDialog(app, crId, crTitle)
            % 打开CR审批对话框
            try
                % 创建审批对话框
                app.createCRApprovalDialog(crId, crTitle);
                
            catch ME
                uialert(app.MainFigure, sprintf('打开CR审批对话框失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function createPRDetailDialog(app, prDetail)
            % 创建PR详情查看对话框
            try
                % 获取当前主题的颜色方案
                colors = app.getCurrentThemeColors();
                
                % 提取标题（从problemDescription获取第一个元素作为标题）
                titleText = '';
                if isfield(prDetail, 'problemDescription')
                    if iscell(prDetail.problemDescription) && ~isempty(prDetail.problemDescription)
                        titleText = prDetail.problemDescription{1};
                    elseif ischar(prDetail.problemDescription) || isstring(prDetail.problemDescription)
                        titleText = char(prDetail.problemDescription);
                    end
                elseif isfield(prDetail, 'issueNumber')
                    titleText = char(prDetail.issueNumber);
                end
                
                % 创建模态对话框窗口
                prDetailDialog = uifigure('Name', sprintf('PR详情 - %s', string(prDetail.issueNumber)), ...
                    'Position', [300, 200, 900, 700], ...
                    'Resize', 'on', ...
                    'WindowStyle', 'modal');
                
                % 对齐主界面原生主题（MATLAB 2025a）
                if isprop(prDetailDialog, 'Theme') && isprop(app.MainFigure, 'Theme')
                    prDetailDialog.Theme = app.MainFigure.Theme;
                    prDetailDialog.ThemeMode = "manual";
                end
                % 应用品牌主题背景色
                prDetailDialog.Color = colors.dialogBg;
                
                % 确保对话框位于最上层（DO-178第8条规则：窗口层级管理）
                figure(prDetailDialog);
                
                % 创建主网格布局（13行2列）
                mainGrid = uigridlayout(prDetailDialog, [13, 2]);
                mainGrid.BackgroundColor = colors.dialogBg;
                mainGrid.RowHeight = {'fit', 'fit', 'fit', 'fit', '1x', '1x', '1x', 'fit', 'fit', 'fit', 'fit', 'fit', 'fit'};
                mainGrid.ColumnWidth = {'1.3x', '2.5x'};
                mainGrid.Padding = [25, 25, 25, 25];
                mainGrid.RowSpacing = 12;
                mainGrid.ColumnSpacing = 20;
                
                % 第1行：数据库ID（只读）
                label1 = uilabel(mainGrid, 'Text', '数据库ID:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label1.Layout.Row = 1;
                label1.Layout.Column = 1;
                idField = uieditfield(mainGrid, 'text', ...
                    'Value', string(prDetail.id), 'Editable', 'off', ...
                    'BackgroundColor', [0.95 0.95 0.95], 'FontColor', [0.4 0.4 0.4]);
                idField.Layout.Row = 1;
                idField.Layout.Column = 2;
                
                % 第2行：PR编号
                label2 = uilabel(mainGrid, 'Text', 'PR编号:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label2.Layout.Row = 2;
                label2.Layout.Column = 1;
                issueNumberField = uieditfield(mainGrid, 'text', ...
                    'Value', string(prDetail.issueNumber), 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                issueNumberField.Layout.Row = 2;
                issueNumberField.Layout.Column = 2;
                
                % 第3行：标题（简要描述）
                label3 = uilabel(mainGrid, 'Text', '标题:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label3.Layout.Row = 3;
                label3.Layout.Column = 1;
                titleField = uieditfield(mainGrid, 'text', ...
                    'Value', titleText, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                titleField.Layout.Row = 3;
                titleField.Layout.Column = 2;
                
                % 第4行：状态（优先使用approval字段）
                label4 = uilabel(mainGrid, 'Text', '状态:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label4.Layout.Row = 4;
                label4.Layout.Column = 1;
                if isfield(prDetail, 'approval')
                    statusValue = string(prDetail.approval);
                elseif isfield(prDetail, 'status')
                    statusValue = string(prDetail.status);
                else
                    error('PR详情缺少必需的状态字段(approval或status)');
                end
                statusField = uieditfield(mainGrid, 'text', ...
                    'Value', statusValue, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                statusField.Layout.Row = 4;
                statusField.Layout.Column = 2;
                
                % 第5行：问题描述（使用problemDescription字段）
                label5 = uilabel(mainGrid, 'Text', '问题描述:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, ...
                    'VerticalAlignment', 'top');
                label5.Layout.Row = 5;
                label5.Layout.Column = 1;
                % 将cell数组转换为多行文本
                descriptionText = '';
                if isfield(prDetail, 'problemDescription')
                    if iscell(prDetail.problemDescription) && ~isempty(prDetail.problemDescription)
                        descriptionText = strjoin(cellfun(@char, prDetail.problemDescription, 'UniformOutput', false), newline);
                    elseif ischar(prDetail.problemDescription) || isstring(prDetail.problemDescription)
                        descriptionText = char(prDetail.problemDescription);
                    end
                end
                descriptionArea = uitextarea(mainGrid, ...
                    'Value', descriptionText, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                descriptionArea.Layout.Row = 5;
                descriptionArea.Layout.Column = 2;
                
                % 第6行：问题机理
                label6 = uilabel(mainGrid, 'Text', '问题机理:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, ...
                    'VerticalAlignment', 'top');
                label6.Layout.Row = 6;
                label6.Layout.Column = 1;
                % 将cell数组转换为多行文本
                mechanismText = '';
                if isfield(prDetail, 'problemMechanism')
                    if iscell(prDetail.problemMechanism) && ~isempty(prDetail.problemMechanism)
                        mechanismText = strjoin(cellfun(@char, prDetail.problemMechanism, 'UniformOutput', false), newline);
                    elseif ischar(prDetail.problemMechanism) || isstring(prDetail.problemMechanism)
                        mechanismText = char(prDetail.problemMechanism);
                    end
                end
                mechanismArea = uitextarea(mainGrid, ...
                    'Value', mechanismText, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                mechanismArea.Layout.Row = 6;
                mechanismArea.Layout.Column = 2;
                
                % 第7行：验证状态
                label7 = uilabel(mainGrid, 'Text', '验证状态:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, ...
                    'VerticalAlignment', 'top');
                label7.Layout.Row = 7;
                label7.Layout.Column = 1;
                % 将cell数组转换为多行文本
                if iscell(prDetail.verificationStatus) && ~isempty(prDetail.verificationStatus)
                    verificationText = strjoin(cellfun(@char, prDetail.verificationStatus, 'UniformOutput', false), newline);
                else
                    verificationText = char(prDetail.verificationStatus);
                end
                verificationArea = uitextarea(mainGrid, ...
                    'Value', verificationText, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                verificationArea.Layout.Row = 7;
                verificationArea.Layout.Column = 2;
                
                % 第8行：严重性
                label8 = uilabel(mainGrid, 'Text', '严重性:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label8.Layout.Row = 8;
                label8.Layout.Column = 1;
                % 优先显示confirmedSeverity，如果没有则显示initialSeverity
                severityValue = '';
                if isfield(prDetail, 'confirmedSeverity') && ~isempty(prDetail.confirmedSeverity)
                    severityValue = string(prDetail.confirmedSeverity);
                elseif isfield(prDetail, 'initialSeverity') && ~isempty(prDetail.initialSeverity)
                    severityValue = string(prDetail.initialSeverity);
                end
                severityField = uieditfield(mainGrid, 'text', ...
                    'Value', severityValue, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                severityField.Layout.Row = 8;
                severityField.Layout.Column = 2;
                
                % 第9行：报告人
                label9 = uilabel(mainGrid, 'Text', '报告人:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label9.Layout.Row = 9;
                label9.Layout.Column = 1;
                reporterField = uieditfield(mainGrid, 'text', ...
                    'Value', string(prDetail.reporter), 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                reporterField.Layout.Row = 9;
                reporterField.Layout.Column = 2;
                
                % 第10行：分配给
                label10 = uilabel(mainGrid, 'Text', '分配给:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label10.Layout.Row = 10;
                label10.Layout.Column = 1;
                assigneeField = uieditfield(mainGrid, 'text', ...
                    'Value', string(prDetail.assignee), 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                assigneeField.Layout.Row = 10;
                assigneeField.Layout.Column = 2;
                
                % 第11行：基线版本
                label11 = uilabel(mainGrid, 'Text', '基线版本:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label11.Layout.Row = 11;
                label11.Layout.Column = 1;
                baselineField = uieditfield(mainGrid, 'text', ...
                    'Value', string(prDetail.baseline), 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                baselineField.Layout.Row = 11;
                baselineField.Layout.Column = 2;
                
                % 第12行：创建时间（优先使用reportDate）
                label12 = uilabel(mainGrid, 'Text', '创建时间:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label12.Layout.Row = 12;
                label12.Layout.Column = 1;
                if isfield(prDetail, 'reportDate')
                    dateValue = string(prDetail.reportDate);
                elseif isfield(prDetail, 'created_at')
                    dateValue = string(prDetail.created_at);
                else
                    error('PR详情缺少必需的日期字段(reportDate或created_at)');
                end
                createdAtField = uieditfield(mainGrid, 'text', ...
                    'Value', dateValue, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                createdAtField.Layout.Row = 12;
                createdAtField.Layout.Column = 2;
                
                % 第13行：按钮区域
                buttonPanel = uipanel(mainGrid, 'BackgroundColor', colors.dialogBg, 'BorderType', 'none');
                buttonPanel.Layout.Row = 13;
                buttonPanel.Layout.Column = [1, 2];
                
                buttonGrid = uigridlayout(buttonPanel, [1, 3]);
                buttonGrid.ColumnWidth = {'1x', 100, 100};
                buttonGrid.Padding = [0, 10, 0, 0];
                buttonGrid.ColumnSpacing = 15;
                
                % 空白区域（弹性）
                emptyLabel = uilabel(buttonGrid, 'Text', '');
                emptyLabel.Layout.Row = 1;
                emptyLabel.Layout.Column = 1;
                
                % 审批按钮（仅admin可见）
                approveBtn = uibutton(buttonGrid, 'push');
                approveBtn.Layout.Row = 1;
                approveBtn.Layout.Column = 2;
                approveBtn.Text = '✅ 审批';
                approveBtn.BackgroundColor = [0.2 0.6 0.2];
                approveBtn.FontColor = [1 1 1];
                approveBtn.FontWeight = 'bold';
                approveBtn.ButtonPushedFcn = @(btn,event) app.openPRApprovalDialog(prDetail.id, titleText);
                % 根据用户权限设置可见性
                if ~app.isCurrentUserAdmin()
                    approveBtn.Visible = 'off';
                end
                
                % 关闭按钮
                closeBtn = uibutton(buttonGrid, 'push');
                closeBtn.Layout.Row = 1;
                closeBtn.Layout.Column = 3;
                closeBtn.Text = '❌ 关闭';
                closeBtn.BackgroundColor = colors.cancelBtn;
                closeBtn.FontColor = [1 1 1];
                closeBtn.FontWeight = 'bold';
                closeBtn.ButtonPushedFcn = @(btn,event) close(prDetailDialog);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                uialert(app.MainFigure, sprintf('创建PR详情对话框失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        function createCRDetailDialog(app, crDetail)
            % 创建CR详情查看对话框
            try
                % 获取当前主题的颜色方案
                colors = app.getCurrentThemeColors();
                
                % 提取标题（从changeDescription获取第一个元素作为标题）
                titleText = '';
                if iscell(crDetail.changeDescription) && ~isempty(crDetail.changeDescription)
                    titleText = crDetail.changeDescription{1};
                elseif ischar(crDetail.changeDescription) || isstring(crDetail.changeDescription)
                    titleText = char(crDetail.changeDescription);
                end
                
                % 创建模态对话框窗口
                crDetailDialog = uifigure('Name', sprintf('CR详情 - %s', string(crDetail.ticketNumber)), ...
                    'Position', [350, 250, 900, 700], ...
                    'Resize', 'on', ...
                    'WindowStyle', 'modal');
                
                % 对齐主界面原生主题（MATLAB 2025a）
                if isprop(crDetailDialog, 'Theme') && isprop(app.MainFigure, 'Theme')
                    crDetailDialog.Theme = app.MainFigure.Theme;
                    crDetailDialog.ThemeMode = "manual";
                end
                % 应用品牌主题背景色
                crDetailDialog.Color = colors.dialogBg;
                
                % 确保对话框位于最上层（DO-178第8条规则：窗口层级管理）
                figure(crDetailDialog);
                
                % 创建主网格布局（13行2列）
                mainGrid = uigridlayout(crDetailDialog, [13, 2]);
                mainGrid.BackgroundColor = colors.dialogBg;
                mainGrid.RowHeight = {'fit', 'fit', 'fit', 'fit', '1x', '1x', '1x', 'fit', 'fit', 'fit', 'fit', 'fit', 'fit'};
                mainGrid.ColumnWidth = {'1.3x', '2.5x'};
                mainGrid.Padding = [25, 25, 25, 25];
                mainGrid.RowSpacing = 12;
                mainGrid.ColumnSpacing = 20;
                
                % 第1行：数据库ID（只读）
                label1 = uilabel(mainGrid, 'Text', '数据库ID:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label1.Layout.Row = 1;
                label1.Layout.Column = 1;
                idField = uieditfield(mainGrid, 'text', ...
                    'Value', string(crDetail.id), 'Editable', 'off', ...
                    'BackgroundColor', [0.95 0.95 0.95], 'FontColor', [0.4 0.4 0.4]);
                idField.Layout.Row = 1;
                idField.Layout.Column = 2;
                
                % 第2行：CR编号
                label2 = uilabel(mainGrid, 'Text', 'CR编号:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label2.Layout.Row = 2;
                label2.Layout.Column = 1;
                ticketNumberField = uieditfield(mainGrid, 'text', ...
                    'Value', string(crDetail.ticketNumber), 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                ticketNumberField.Layout.Row = 2;
                ticketNumberField.Layout.Column = 2;
                
                % 第3行：标题（简要描述）
                label3 = uilabel(mainGrid, 'Text', '标题:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label3.Layout.Row = 3;
                label3.Layout.Column = 1;
                titleField = uieditfield(mainGrid, 'text', ...
                    'Value', titleText, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                titleField.Layout.Row = 3;
                titleField.Layout.Column = 2;
                
                % 第4行：状态（优先使用approval字段）
                label4 = uilabel(mainGrid, 'Text', '状态:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label4.Layout.Row = 4;
                label4.Layout.Column = 1;
                if isfield(crDetail, 'approval')
                    statusValue = string(crDetail.approval);
                elseif isfield(crDetail, 'status')
                    statusValue = string(crDetail.status);
                else
                    error('CR详情缺少必需的状态字段(approval或status)');
                end
                statusField = uieditfield(mainGrid, 'text', ...
                    'Value', statusValue, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                statusField.Layout.Row = 4;
                statusField.Layout.Column = 2;
                
                % 第5行：来源/分类（CR特有字段，可选）
                label5 = uilabel(mainGrid, 'Text', '来源:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, ...
                    'VerticalAlignment', 'top');
                label5.Layout.Row = 5;
                label5.Layout.Column = 1;
                % 根据DO-178第2条规则：明确错误处理
                % CR使用changeSource字段（可选），不是source字段
                % 根据SRS.md，changeSource是可选字段，如果不存在显示空值
                if isfield(crDetail, 'changeSource') && ~isempty(crDetail.changeSource)
                    sourceValue = string(crDetail.changeSource);
                else
                    sourceValue = '';  % 可选字段，明确显示为空
                end
                sourceArea = uitextarea(mainGrid, ...
                    'Value', sourceValue, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                sourceArea.Layout.Row = 5;
                sourceArea.Layout.Column = 2;
                
                % 第6行：变更描述
                label6 = uilabel(mainGrid, 'Text', '变更描述:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, ...
                    'VerticalAlignment', 'top');
                label6.Layout.Row = 6;
                label6.Layout.Column = 1;
                % 将cell数组转换为多行文本
                if iscell(crDetail.changeDescription) && ~isempty(crDetail.changeDescription)
                    changeDescText = strjoin(cellfun(@char, crDetail.changeDescription, 'UniformOutput', false), newline);
                else
                    changeDescText = char(crDetail.changeDescription);
                end
                changeDescArea = uitextarea(mainGrid, ...
                    'Value', changeDescText, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                changeDescArea.Layout.Row = 6;
                changeDescArea.Layout.Column = 2;
                
                % 第7行：基线版本
                label7 = uilabel(mainGrid, 'Text', '基线版本:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, ...
                    'VerticalAlignment', 'top');
                label7.Layout.Row = 7;
                label7.Layout.Column = 1;
                baselineArea = uitextarea(mainGrid, ...
                    'Value', string(crDetail.baseline), 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                baselineArea.Layout.Row = 7;
                baselineArea.Layout.Column = 2;
                
                % 第8行：严重性（可选字段）
                label8 = uilabel(mainGrid, 'Text', '严重性:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label8.Layout.Row = 8;
                label8.Layout.Column = 1;
                % 根据DO-178第2条规则：明确错误处理
                % CR数据结构中severity是可选字段（根据SRS.md）
                if isfield(crDetail, 'severity') && ~isempty(crDetail.severity)
                    severityValue = string(crDetail.severity);
                else
                    severityValue = '';  % 可选字段，明确显示为空
                end
                severityField = uieditfield(mainGrid, 'text', ...
                    'Value', severityValue, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                severityField.Layout.Row = 8;
                severityField.Layout.Column = 2;
                
                % 第9行：项目名称
                label9 = uilabel(mainGrid, 'Text', '项目:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label9.Layout.Row = 9;
                label9.Layout.Column = 1;
                projectField = uieditfield(mainGrid, 'text', ...
                    'Value', string(crDetail.project), 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                projectField.Layout.Row = 9;
                projectField.Layout.Column = 2;
                
                % 第10行：报告人
                label10 = uilabel(mainGrid, 'Text', '报告人:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label10.Layout.Row = 10;
                label10.Layout.Column = 1;
                reporterField = uieditfield(mainGrid, 'text', ...
                    'Value', string(crDetail.reporter), 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                reporterField.Layout.Row = 10;
                reporterField.Layout.Column = 2;
                
                % 第11行：分配给
                label11 = uilabel(mainGrid, 'Text', '分配给:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label11.Layout.Row = 11;
                label11.Layout.Column = 1;
                assigneeField = uieditfield(mainGrid, 'text', ...
                    'Value', string(crDetail.assignee), 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                assigneeField.Layout.Row = 11;
                assigneeField.Layout.Column = 2;
                
                % 第12行：创建时间（优先使用reportDate）
                label12 = uilabel(mainGrid, 'Text', '创建时间:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                label12.Layout.Row = 12;
                label12.Layout.Column = 1;
                if isfield(crDetail, 'reportDate')
                    dateValue = string(crDetail.reportDate);
                elseif isfield(crDetail, 'created_at')
                    dateValue = string(crDetail.created_at);
                else
                    error('CR详情缺少必需的日期字段(reportDate或created_at)');
                end
                createdAtField = uieditfield(mainGrid, 'text', ...
                    'Value', dateValue, 'Editable', 'off', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                createdAtField.Layout.Row = 12;
                createdAtField.Layout.Column = 2;
                
                % 第13行：按钮区域
                buttonPanel = uipanel(mainGrid, 'BackgroundColor', colors.dialogBg, 'BorderType', 'none');
                buttonPanel.Layout.Row = 13;
                buttonPanel.Layout.Column = [1, 2];
                
                buttonGrid = uigridlayout(buttonPanel, [1, 3]);
                buttonGrid.ColumnWidth = {'1x', 100, 100};
                buttonGrid.Padding = [0, 10, 0, 0];
                buttonGrid.ColumnSpacing = 15;
                
                % 空白区域（弹性）
                emptyLabel = uilabel(buttonGrid, 'Text', '');
                emptyLabel.Layout.Row = 1;
                emptyLabel.Layout.Column = 1;
                
                % 审批按钮（仅admin可见）
                approveBtn = uibutton(buttonGrid, 'push');
                approveBtn.Layout.Row = 1;
                approveBtn.Layout.Column = 2;
                approveBtn.Text = '✅ 审批';
                approveBtn.BackgroundColor = [0.2 0.6 0.2];
                approveBtn.FontColor = [1 1 1];
                approveBtn.FontWeight = 'bold';
                approveBtn.ButtonPushedFcn = @(btn,event) app.openCRApprovalDialog(crDetail.id, titleText);
                % 根据用户权限设置可见性
                if ~app.isCurrentUserAdmin()
                    approveBtn.Visible = 'off';
                end
                
                % 关闭按钮
                closeBtn = uibutton(buttonGrid, 'push');
                closeBtn.Layout.Row = 1;
                closeBtn.Layout.Column = 3;
                closeBtn.Text = '❌ 关闭';
                closeBtn.BackgroundColor = colors.cancelBtn;
                closeBtn.FontColor = [1 1 1];
                closeBtn.FontWeight = 'bold';
                closeBtn.ButtonPushedFcn = @(btn,event) close(crDetailDialog);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                uialert(app.MainFigure, sprintf('创建CR详情对话框失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function createPRApprovalDialog(app, prId, prTitle)
            % 创建PR审批表单对话框 - API v2.0规格
            % 2明确错误处理，不使用fallback机制
            % 支持多级顺序审批流程
            try
                % 获取当前主题的颜色方案
                colors = app.getCurrentThemeColors();
                
                % 创建模态对话框窗口
                approvalDialog = uifigure('Name', sprintf('PR审批 - ID:%d - API v2.0', prId), ...
                    'Position', [400, 250, 750, 550], ...
                    'Resize', 'on', ...
                    'WindowStyle', 'modal');
                
                % 对齐主界面原生主题（MATLAB 2025a）
                if isprop(approvalDialog, 'Theme') && isprop(app.MainFigure, 'Theme')
                    approvalDialog.Theme = app.MainFigure.Theme;
                    approvalDialog.ThemeMode = "manual";
                end
                % 应用品牌主题背景色
                approvalDialog.Color = colors.dialogBg;
                
                % 确保对话框位于最上层（DO-178第8条规则：窗口层级管理）
                figure(approvalDialog);
                
                % 创建主网格布局（7行2列）
                mainGrid = uigridlayout(approvalDialog, [7, 2]);
                mainGrid.BackgroundColor = colors.dialogBg;
                mainGrid.RowHeight = {'fit', 'fit', 'fit', 'fit', '1x', 'fit', 'fit'};
                mainGrid.ColumnWidth = {'1.3x', '3x'};
                mainGrid.Padding = [25, 25, 25, 25];
                mainGrid.RowSpacing = 15;
                mainGrid.ColumnSpacing = 20;
                
                % 第1行：PR ID 和 标题显示
                label1 = uilabel(mainGrid, 'Text', 'PR信息:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, 'FontSize', 14);
                label1.Layout.Row = 1;
                label1.Layout.Column = 1;
                infoField = uieditfield(mainGrid, 'text', ...
                    'Value', sprintf('ID: %d | %s', prId, prTitle), 'Editable', 'off', ...
                    'BackgroundColor', [0.95 0.95 0.95], 'FontColor', [0.4 0.4 0.4], 'FontWeight', 'bold');
                infoField.Layout.Row = 1;
                infoField.Layout.Column = 2;
                
                % 第2行：多级审批进度（动态加载）
                label2 = uilabel(mainGrid, 'Text', '审批进度:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, 'FontSize', 12);
                label2.Layout.Row = 2;
                label2.Layout.Column = 1;
                progressField = uieditfield(mainGrid, 'text', ...
                    'Value', '正在加载审批进度...', 'Editable', 'off', ...
                    'BackgroundColor', [0.9 0.95 1.0], 'FontColor', [0.2 0.4 0.8], 'FontWeight', 'bold');
                progressField.Layout.Row = 2;
                progressField.Layout.Column = 2;
                
                % 第3行：审批状态选择（符合API规格：APPROVED/REJECTED）
                label3 = uilabel(mainGrid, 'Text', '* 审批结果:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2], 'FontSize', 12);
                label3.Layout.Row = 3;
                label3.Layout.Column = 1;
                approvalStatusDD = uidropdown(mainGrid, ...
                    'Items', {'APPROVED (批准)', 'REJECTED (拒绝)'}, ...
                    'ItemsData', {'APPROVED', 'REJECTED'}, ...
                    'Value', 'APPROVED', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText, ...
                    'FontWeight', 'bold');
                approvalStatusDD.Layout.Row = 3;
                approvalStatusDD.Layout.Column = 2;
                
                % 第4行：审批人（只读，自动填充当前用户）
                label4 = uilabel(mainGrid, 'Text', '审批人:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, 'FontSize', 12);
                label4.Layout.Row = 4;
                label4.Layout.Column = 1;
                approverField = uieditfield(mainGrid, 'text', ...
                    'Value', app.getCurrentUserName(), 'Editable', 'off', ...
                    'BackgroundColor', [0.95 0.95 0.95], 'FontColor', [0.4 0.4 0.4]);
                approverField.Layout.Row = 4;
                approverField.Layout.Column = 2;
                
                % 第5行：审批意见（多行文本区域，必填）
                label5 = uilabel(mainGrid, 'Text', '* 审批意见:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2], 'FontSize', 12, ...
                    'VerticalAlignment', 'top');
                label5.Layout.Row = 5;
                label5.Layout.Column = 1;
                commentArea = uitextarea(mainGrid, ...
                    'Value', '', ...
                    'Placeholder', '请输入审批意见（必填）...', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                commentArea.Layout.Row = 5;
                commentArea.Layout.Column = 2;
                
                % 第6行：审批说明（系统自动处理）
                label6 = uilabel(mainGrid, 'Text', '', ...
                    'FontWeight', 'normal', 'FontColor', [0.5 0.5 0.5], 'FontSize', 10);
                label6.Layout.Row = 6;
                label6.Layout.Column = [1, 2];
                label6.Text = sprintf(['说明：\n' ...
                    '• 审批时间由系统自动记录\n' ...
                    '• 顺序审批：通过后自动进入下一级，拒绝立即结束流程\n' ...
                    '• 只有当前级别的审批人可以审批']);
                
                % 第7行：按钮区域
                buttonPanel = uipanel(mainGrid, 'BackgroundColor', colors.dialogBg, 'BorderType', 'none');
                buttonPanel.Layout.Row = 7;
                buttonPanel.Layout.Column = [1, 2];
                
                buttonGrid = uigridlayout(buttonPanel, [1, 3]);
                buttonGrid.ColumnWidth = {'1x', 130, 120};
                buttonGrid.Padding = [0, 10, 0, 0];
                buttonGrid.ColumnSpacing = 15;
                
                % 空白区域（弹性）
                emptyBtn = uilabel(buttonGrid, 'Text', '');
                emptyBtn.Layout.Row = 1;
                emptyBtn.Layout.Column = 1;
                
                % 提交审批按钮
                submitBtn = uibutton(buttonGrid, 'push');
                submitBtn.Layout.Row = 1;
                submitBtn.Layout.Column = 2;
                submitBtn.Text = '✅ 提交审批';
                submitBtn.BackgroundColor = [0.2 0.6 0.8];
                submitBtn.FontColor = [1 1 1];
                submitBtn.FontWeight = 'bold';
                submitBtn.ButtonPushedFcn = @(btn,event) submitPRApprovalCallback();
                
                % 取消按钮
                cancelBtn = uibutton(buttonGrid, 'push');
                cancelBtn.Layout.Row = 1;
                cancelBtn.Layout.Column = 3;
                cancelBtn.Text = '❌ 取消';
                cancelBtn.BackgroundColor = colors.cancelBtn;
                cancelBtn.FontColor = [1 1 1];
                cancelBtn.FontWeight = 'bold';
                cancelBtn.ButtonPushedFcn = @(btn,event) close(approvalDialog);
                
                % 异步加载审批进度（显示当前审批级别、总级数等）
                app.loadPRApprovalProgress(prId, progressField);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                uialert(app.MainFigure, sprintf('创建PR审批对话框失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
            
            % 内嵌审批提交回调函数（必须在主函数末尾）
            function submitPRApprovalCallback()
                try
                    % ========== 数据验证 ==========
                    % 2明确验证，不使用fallback
                    
                    % 验证审批意见（必填）
                    % uitextarea.Value返回cell array，需要转换为字符串
                    if iscell(commentArea.Value)
                        commentValue = strjoin(commentArea.Value, '\n');  % 多行文本用换行符连接
                    else
                        commentValue = string(commentArea.Value);
                    end
                    commentValue = strtrim(commentValue);
                    
                    if isempty(commentValue)
                        uialert(approvalDialog, '❌ 审批意见不能为空！', '表单验证', 'Icon', 'warning');
                        return;
                    end
                    
                    % 获取审批状态（APPROVED/REJECTED）
                    approvalStatus = approvalStatusDD.Value;
                    
                    % 显示进度条
                    progressDlg = uiprogressdlg(approvalDialog, 'Title', 'PR审批', ...
                        'Message', sprintf('正在提交PR审批 (API v2.0) - %s...', approvalStatus), ...
                        'Indeterminate', 'on');
                    
                    try
                        % 调用ALM服务提交审批
                        % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                        if isempty(app.ALMService)
                            app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                        end
                        [success, response] = app.ALMService.approvePR(prId, approvalStatus, commentValue);
                        
                        close(progressDlg);
                        
                        if success
                            % 审批成功
                            % 构建成功消息（包含审批进度信息）
                            successMsg = sprintf('✅ PR #%d 审批提交成功！\n\n', prId);
                            if isfield(response, 'message')
                                successMsg = sprintf('%s%s\n\n', successMsg, response.message);
                            end
                            if isfield(response, 'data')
                                data = response.data;
                                if isfield(data, 'approval_level') && isfield(data, 'required_levels')
                                    successMsg = sprintf('%s审批进度：第 %d 级 / 共 %d 级\n', ...
                                        successMsg, data.approval_level, data.required_levels);
                                end
                                if isfield(data, 'is_completed') && data.is_completed
                                    successMsg = sprintf('%s🎉 所有审批已完成！', successMsg);
                                elseif isfield(data, 'current_level')
                                    successMsg = sprintf('%s⏭️ 已进入第 %d 级审批', successMsg, data.current_level);
                                end
                            end
                            
                            uialert(approvalDialog, successMsg, 'PR审批成功', 'Icon', 'success');
                            
                            % 更新主界面消息区域
                            if isvalid(app.MessagesTextArea)
                                app.MessagesTextArea.Value = sprintf('✅ PR #%d 审批成功：%s', prId, approvalStatus);
                            end
                            
                            % 关闭对话框并刷新数据
                            close(approvalDialog);
                            app.onRefreshPRCRButtonPushed();
                            
                            % 确保主窗口回到前台
                            % figure(app.MainFigure) #tmpfix;
                            
                        else
                            % 审批失败
                            errorMsg = '❌ PR审批提交失败';
                            if isfield(response, 'error')
                                errorMsg = sprintf('%s：%s', errorMsg, response.error);
                            elseif isfield(response, 'message')
                                errorMsg = sprintf('%s：%s', errorMsg, response.message);
                            end
                            uialert(approvalDialog, errorMsg, 'PR审批失败', 'Icon', 'error');
                            
                            % 更新主界面消息区域
                            if isvalid(app.MessagesTextArea)
                                app.MessagesTextArea.Value = sprintf('❌ PR #%d 审批失败', prId);
                            end
                        end
                        
                    catch networkME
                        close(progressDlg);
                        uialert(approvalDialog, sprintf('PR审批提交失败：%s', networkME.message), ...
                            '网络错误', 'Icon', 'error');
                    end
                    
                catch ME
                    if exist('progressDlg', 'var') && isvalid(progressDlg)
                        close(progressDlg);
                    end
                    uialert(approvalDialog, sprintf('审批数据处理失败：%s', ME.message), '错误', 'Icon', 'error');
                end
            end
        end
        
        function createCRApprovalDialog(app, crId, crTitle)
            % 创建CR审批表单对话框 - API v2.0规格
            % 2明确错误处理，不使用fallback机制
            % 支持多级顺序审批流程
            try
                % 获取当前主题的颜色方案
                colors = app.getCurrentThemeColors();
                
                % 创建模态对话框窗口
                approvalDialog = uifigure('Name', sprintf('CR审批 - ID:%d - API v2.0', crId), ...
                    'Position', [450, 250, 750, 550], ...
                    'Resize', 'on', ...
                    'WindowStyle', 'modal');
                
                % 对齐主界面原生主题（MATLAB 2025a）
                if isprop(approvalDialog, 'Theme') && isprop(app.MainFigure, 'Theme')
                    approvalDialog.Theme = app.MainFigure.Theme;
                    approvalDialog.ThemeMode = "manual";
                end
                % 应用品牌主题背景色
                approvalDialog.Color = colors.dialogBg;
                
                % 确保对话框位于最上层（DO-178第8条规则：窗口层级管理）
                figure(approvalDialog);
                
                % 创建主网格布局（7行2列）
                mainGrid = uigridlayout(approvalDialog, [7, 2]);
                mainGrid.BackgroundColor = colors.dialogBg;
                mainGrid.RowHeight = {'fit', 'fit', 'fit', 'fit', '1x', 'fit', 'fit'};
                mainGrid.ColumnWidth = {'1.3x', '3x'};
                mainGrid.Padding = [25, 25, 25, 25];
                mainGrid.RowSpacing = 15;
                mainGrid.ColumnSpacing = 20;
                
                % 第1行：CR ID 和 标题显示
                label1 = uilabel(mainGrid, 'Text', 'CR信息:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, 'FontSize', 14);
                label1.Layout.Row = 1;
                label1.Layout.Column = 1;
                infoField = uieditfield(mainGrid, 'text', ...
                    'Value', sprintf('ID: %d | %s', crId, crTitle), 'Editable', 'off', ...
                    'BackgroundColor', [0.95 0.95 0.95], 'FontColor', [0.4 0.4 0.4], 'FontWeight', 'bold');
                infoField.Layout.Row = 1;
                infoField.Layout.Column = 2;
                
                % 第2行：多级审批进度（动态加载）
                label2 = uilabel(mainGrid, 'Text', '审批进度:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, 'FontSize', 12);
                label2.Layout.Row = 2;
                label2.Layout.Column = 1;
                progressField = uieditfield(mainGrid, 'text', ...
                    'Value', '正在加载审批进度...', 'Editable', 'off', ...
                    'BackgroundColor', [0.9 0.95 1.0], 'FontColor', [0.2 0.4 0.8], 'FontWeight', 'bold');
                progressField.Layout.Row = 2;
                progressField.Layout.Column = 2;
                
                % 第3行：审批状态选择（符合API规格：APPROVED/REJECTED）
                label3 = uilabel(mainGrid, 'Text', '* 审批结果:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2], 'FontSize', 12);
                label3.Layout.Row = 3;
                label3.Layout.Column = 1;
                approvalStatusDD = uidropdown(mainGrid, ...
                    'Items', {'APPROVED (批准)', 'REJECTED (拒绝)'}, ...
                    'ItemsData', {'APPROVED', 'REJECTED'}, ...
                    'Value', 'APPROVED', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText, ...
                    'FontWeight', 'bold');
                approvalStatusDD.Layout.Row = 3;
                approvalStatusDD.Layout.Column = 2;
                
                % 第4行：审批人（只读，自动填充当前用户）
                label4 = uilabel(mainGrid, 'Text', '审批人:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText, 'FontSize', 12);
                label4.Layout.Row = 4;
                label4.Layout.Column = 1;
                approverField = uieditfield(mainGrid, 'text', ...
                    'Value', app.getCurrentUserName(), 'Editable', 'off', ...
                    'BackgroundColor', [0.95 0.95 0.95], 'FontColor', [0.4 0.4 0.4]);
                approverField.Layout.Row = 4;
                approverField.Layout.Column = 2;
                
                % 第5行：审批意见（多行文本区域，必填）
                label5 = uilabel(mainGrid, 'Text', '* 审批意见:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2], 'FontSize', 12, ...
                    'VerticalAlignment', 'top');
                label5.Layout.Row = 5;
                label5.Layout.Column = 1;
                commentArea = uitextarea(mainGrid, ...
                    'Value', '', ...
                    'Placeholder', '请输入审批意见（必填）...', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                commentArea.Layout.Row = 5;
                commentArea.Layout.Column = 2;
                
                % 第6行：审批说明（系统自动处理）
                label6 = uilabel(mainGrid, 'Text', '', ...
                    'FontWeight', 'normal', 'FontColor', [0.5 0.5 0.5], 'FontSize', 10);
                label6.Layout.Row = 6;
                label6.Layout.Column = [1, 2];
                label6.Text = sprintf(['说明：\n' ...
                    '• 审批时间由系统自动记录\n' ...
                    '• 顺序审批：通过后自动进入下一级，拒绝立即结束流程\n' ...
                    '• 只有当前级别的审批人可以审批']);
                
                % 第7行：按钮区域
                buttonPanel = uipanel(mainGrid, 'BackgroundColor', colors.dialogBg, 'BorderType', 'none');
                buttonPanel.Layout.Row = 7;
                buttonPanel.Layout.Column = [1, 2];
                
                buttonGrid = uigridlayout(buttonPanel, [1, 3]);
                buttonGrid.ColumnWidth = {'1x', 130, 120};
                buttonGrid.Padding = [0, 10, 0, 0];
                buttonGrid.ColumnSpacing = 15;
                
                % 空白区域（弹性）
                emptyBtn = uilabel(buttonGrid, 'Text', '');
                emptyBtn.Layout.Row = 1;
                emptyBtn.Layout.Column = 1;
                
                % 提交审批按钮
                submitBtn = uibutton(buttonGrid, 'push');
                submitBtn.Layout.Row = 1;
                submitBtn.Layout.Column = 2;
                submitBtn.Text = '✅ 提交审批';
                submitBtn.BackgroundColor = [0.6 0.2 0.6];
                submitBtn.FontColor = [1 1 1];
                submitBtn.FontWeight = 'bold';
                submitBtn.ButtonPushedFcn = @(btn,event) submitCRApprovalCallback();
                
                % 取消按钮
                cancelBtn = uibutton(buttonGrid, 'push');
                cancelBtn.Layout.Row = 1;
                cancelBtn.Layout.Column = 3;
                cancelBtn.Text = '❌ 取消';
                cancelBtn.BackgroundColor = colors.cancelBtn;
                cancelBtn.FontColor = [1 1 1];
                cancelBtn.FontWeight = 'bold';
                cancelBtn.ButtonPushedFcn = @(btn,event) close(approvalDialog);
                
                % 异步加载审批进度（显示当前审批级别、总级数等）
                app.loadCRApprovalProgress(crId, progressField);
                
                % 确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                uialert(app.MainFigure, sprintf('创建CR审批对话框失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
            
            % 内嵌审批提交回调函数（必须在主函数末尾）
            function submitCRApprovalCallback()
                try
                    % ========== 数据验证 ==========
                    % 2明确验证，不使用fallback
                    
                    % 验证审批意见（必填）
                    % uitextarea.Value返回cell array，需要转换为字符串
                    if iscell(commentArea.Value)
                        commentValue = strjoin(commentArea.Value, '\n');  % 多行文本用换行符连接
                    else
                        commentValue = string(commentArea.Value);
                    end
                    commentValue = strtrim(commentValue);
                    
                    if isempty(commentValue)
                        uialert(approvalDialog, '❌ 审批意见不能为空！', '表单验证', 'Icon', 'warning');
                        return;
                    end
                    
                    % 获取审批状态（APPROVED/REJECTED）
                    approvalStatus = approvalStatusDD.Value;
                    
                    % 显示进度条
                    progressDlg = uiprogressdlg(approvalDialog, 'Title', 'CR审批', ...
                        'Message', sprintf('正在提交CR审批 (API v2.0) - %s...', approvalStatus), ...
                        'Indeterminate', 'on');
                    
                    try
                        % 调用ALM服务提交审批
                        % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                        if isempty(app.ALMService)
                            app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                        end
                        [success, response] = app.ALMService.approveCR(crId, approvalStatus, commentValue);
                        
                        close(progressDlg);
                        
                        if success
                            % 审批成功
                            % 构建成功消息（包含审批进度信息）
                            successMsg = sprintf('✅ CR #%d 审批提交成功！\n\n', crId);
                            if isfield(response, 'message')
                                successMsg = sprintf('%s%s\n\n', successMsg, response.message);
                            end
                            if isfield(response, 'data')
                                data = response.data;
                                if isfield(data, 'approval_level') && isfield(data, 'required_levels')
                                    successMsg = sprintf('%s审批进度：第 %d 级 / 共 %d 级\n', ...
                                        successMsg, data.approval_level, data.required_levels);
                                end
                                if isfield(data, 'is_completed') && data.is_completed
                                    successMsg = sprintf('%s🎉 所有审批已完成！', successMsg);
                                elseif isfield(data, 'current_level')
                                    successMsg = sprintf('%s⏭️ 已进入第 %d 级审批', successMsg, data.current_level);
                                end
                            end
                            
                            uialert(approvalDialog, successMsg, 'CR审批成功', 'Icon', 'success');
                            
                            % 更新主界面消息区域
                            if isvalid(app.MessagesTextArea)
                                app.MessagesTextArea.Value = sprintf('✅ CR #%d 审批成功：%s', crId, approvalStatus);
                            end
                            
                            % 关闭对话框并刷新数据
                            close(approvalDialog);
                            app.onRefreshPRCRButtonPushed();
                            
                            % 确保主窗口回到前台
                            % figure(app.MainFigure) #tmpfix;
                            
                        else
                            % 审批失败
                            errorMsg = '❌ CR审批提交失败';
                            if isfield(response, 'error')
                                errorMsg = sprintf('%s：%s', errorMsg, response.error);
                            elseif isfield(response, 'message')
                                errorMsg = sprintf('%s：%s', errorMsg, response.message);
                            end
                            uialert(approvalDialog, errorMsg, 'CR审批失败', 'Icon', 'error');
                            
                            % 更新主界面消息区域
                            if isvalid(app.MessagesTextArea)
                                app.MessagesTextArea.Value = sprintf('❌ CR #%d 审批失败', crId);
                            end
                        end
                        
                    catch networkME
                        close(progressDlg);
                        uialert(approvalDialog, sprintf('CR审批提交失败：%s', networkME.message), ...
                            '网络错误', 'Icon', 'error');
                    end
                    
                catch ME
                    if exist('progressDlg', 'var') && isvalid(progressDlg)
                        close(progressDlg);
                    end
                    uialert(approvalDialog, sprintf('审批数据处理失败：%s', ME.message), '错误', 'Icon', 'error');
                end
            end
        end
        % 以下数据转换方法已迁移到ALMDataTransformer（基于规则第4条）
        % convertPRDataToTable -> ALMDataTransformer.convertPRDataToTable
        % convertCRDataToTable -> ALMDataTransformer.convertCRDataToTable
        % processPRRecord -> ALMDataTransformer.processPRRecord
        % processCRRecord -> ALMDataTransformer.processCRRecord
        function createPRSubmissionDialog(app)
            % 创建PR提交表单对话框 - 符合API v2.0规格（15个字段：11必填+4可选）
            % 3项目/基线/人员字段从服务器动态加载
            try
                % 获取当前主题的颜色方案
                colors = app.getCurrentThemeColors();
                
                % 获取屏幕高度，设置窗口占满屏幕高度
                screenSize = get(0, 'ScreenSize');
                screenHeight = screenSize(4);
                dialogHeight = screenHeight - 80;  % 留出顶部和底部边距
                
                % 创建模态对话框窗口（高度最大化，宽度保持不变）
                prDialog = uifigure('Name', '问题报告 (PR) 提交表单 - API v2.0', ...
                    'Position', [100, 40, 950, dialogHeight], ...
                    'Resize', 'on', ...
                    'WindowStyle', 'modal');
                % 对齐主界面原生主题（MATLAB 2025a）
                if isprop(prDialog, 'Theme') && isprop(app.MainFigure, 'Theme')
                    prDialog.Theme = app.MainFigure.Theme;
                    prDialog.ThemeMode = "manual";
                end
                % 应用品牌浅/深主题背景色
                prDialog.Color = colors.dialogBg;
                
                % 确保对话框位于最上层（DO-178第8条规则：窗口层级管理）
                figure(prDialog);
                
                % 创建主网格布局（包含4个分组面板+按钮区域，共5行）
                mainGrid = uigridlayout(prDialog, [5, 1]);
                mainGrid.BackgroundColor = colors.dialogBg;
                mainGrid.RowHeight = {'fit', 'fit', 'fit', 'fit', 'fit'};
                mainGrid.ColumnWidth = {'1x'};
                mainGrid.Padding = [15, 15, 15, 15];
                mainGrid.RowSpacing = 10;
                
                % ========== 分组1：基础信息（5个必填字段） ==========
                basicInfoPanel = uipanel(mainGrid, 'Title', '★ 基础信息（必填）', ...
                    'FontWeight', 'bold', 'FontSize', 11, ...
                    'BackgroundColor', colors.dialogBg, ...
                    'ForegroundColor', colors.labelText);
                basicInfoPanel.Layout.Row = 1;
                basicInfoPanel.Layout.Column = 1;
                
                basicGrid = uigridlayout(basicInfoPanel, [5, 2]);
                basicGrid.BackgroundColor = colors.dialogBg;
                basicGrid.RowHeight = {'fit', 'fit', 'fit', 'fit', 'fit'};
                basicGrid.ColumnWidth = {150, '1x'};
                basicGrid.Padding = [10, 10, 10, 10];
                basicGrid.RowSpacing = 8;
                basicGrid.ColumnSpacing = 10;
                
                % 项目名称（从服务器动态加载）
                projectLabel = uilabel(basicGrid, 'Text', '* 项目名称:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2]);
                projectLabel.Layout.Row = 1;
                projectLabel.Layout.Column = 1;
                projectDropDown = uidropdown(basicGrid, ...
                    'Items', {'正在加载项目列表...'}, ...
                    'Value', '正在加载项目列表...', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                projectDropDown.Layout.Row = 1;
                projectDropDown.Layout.Column = 2;
                
                % 问题编号（issueNumber）
                issueNumberLabel = uilabel(basicGrid, 'Text', '* 问题编号:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2]);
                issueNumberLabel.Layout.Row = 2;
                issueNumberLabel.Layout.Column = 1;
                issueNumberEdit = uieditfield(basicGrid, 'text', ...
                    'Value', 'E-RAS-IVOS-PR-001', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                issueNumberEdit.Layout.Row = 2;
                issueNumberEdit.Layout.Column = 2;
                
                % 提交人（reporter）
                reporterLabel = uilabel(basicGrid, 'Text', '* 提交人:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2]);
                reporterLabel.Layout.Row = 3;
                reporterLabel.Layout.Column = 1;
                reporterDropDown = uidropdown(basicGrid, ...
                    'Items', {'正在加载用户列表...'}, ...
                    'Value', '正在加载用户列表...', ...
                    'Editable', 'on', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                reporterDropDown.Layout.Row = 3;
                reporterDropDown.Layout.Column = 2;
                
                % 问题来源（source，字符串，无枚举限制）
                sourceLabel = uilabel(basicGrid, 'Text', '* 问题来源:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2]);
                sourceLabel.Layout.Row = 4;
                sourceLabel.Layout.Column = 1;
                sourceEdit = uieditfield(basicGrid, 'text', ...
                    'Value', '开发阶段', ...
                    'Placeholder', '如：开发阶段/集成测试/外部测试', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                sourceEdit.Layout.Row = 4;
                sourceEdit.Layout.Column = 2;
                
                % 初定严重度（initialSeverity，枚举：严重/一般）
                initialSeverityLabel = uilabel(basicGrid, 'Text', '* 初定严重度:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2]);
                initialSeverityLabel.Layout.Row = 5;
                initialSeverityLabel.Layout.Column = 1;
                initialSeverityDropDown = uidropdown(basicGrid, ...
                    'Items', {'严重', '一般'}, ...
                    'Value', '一般', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                initialSeverityDropDown.Layout.Row = 5;
                initialSeverityDropDown.Layout.Column = 2;
                
                % ========== 分组2：问题描述和分析（4个必填字段） ==========
                problemPanel = uipanel(mainGrid, 'Title', '★ 问题描述和分析（必填）', ...
                    'FontWeight', 'bold', 'FontSize', 11, ...
                    'BackgroundColor', colors.dialogBg, ...
                    'ForegroundColor', colors.labelText);
                problemPanel.Layout.Row = 2;
                problemPanel.Layout.Column = 1;
                
                problemGrid = uigridlayout(problemPanel, [4, 2]);
                problemGrid.BackgroundColor = colors.dialogBg;
                problemGrid.RowHeight = {'fit', 'fit', 'fit', 'fit'};
                problemGrid.ColumnWidth = {150, '1x'};
                problemGrid.Padding = [10, 10, 10, 10];
                problemGrid.RowSpacing = 8;
                problemGrid.ColumnSpacing = 10;
                
                % 问题描述（problemDescription，多行文本）
                problemDescLabel = uilabel(problemGrid, 'Text', '* 问题描述:', ...
                    'FontWeight', 'bold', 'VerticalAlignment', 'top', ...
                    'FontColor', [0.8 0.2 0.2]);
                problemDescLabel.Layout.Row = 1;
                problemDescLabel.Layout.Column = 1;
                problemDescArea = uitextarea(problemGrid, ...
                    'Value', '电机启停逻辑错误，Latch模块触发信号和重置信号有可能同时为真', ...
                    'BackgroundColor', colors.textAreaBg, 'FontColor', colors.inputText);
                problemDescArea.Layout.Row = 1;
                problemDescArea.Layout.Column = 2;
                
                % 问题机理和复现情况（problemMechanism，多行文本）
                problemMechanismLabel = uilabel(problemGrid, 'Text', '* 问题机理:', ...
                    'FontWeight', 'bold', 'VerticalAlignment', 'top', ...
                    'FontColor', [0.8 0.2 0.2]);
                problemMechanismLabel.Layout.Row = 2;
                problemMechanismLabel.Layout.Column = 1;
                problemMechanismArea = uitextarea(problemGrid, ...
                    'Value', '不考虑实际场景的前提下，如果飞机处于充电模式，同时又处于空中模式...可以复现。', ...
                    'BackgroundColor', colors.textAreaBg, 'FontColor', colors.inputText);
                problemMechanismArea.Layout.Row = 2;
                problemMechanismArea.Layout.Column = 2;
                
                % 关联需求（relatedRequirements）
                relatedReqLabel = uilabel(problemGrid, 'Text', '* 关联需求:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2]);
                relatedReqLabel.Layout.Row = 3;
                relatedReqLabel.Layout.Column = 1;
                relatedReqEdit = uieditfield(problemGrid, 'text', ...
                    'Value', 'IYOS_HLR_064', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                relatedReqEdit.Layout.Row = 3;
                relatedReqEdit.Layout.Column = 2;
                
                % 影响性分析（impactAnalysis，多行文本）
                impactAnalysisLabel = uilabel(problemGrid, 'Text', '* 影响性分析:', ...
                    'FontWeight', 'bold', 'VerticalAlignment', 'top', ...
                    'FontColor', [0.8 0.2 0.2]);
                impactAnalysisLabel.Layout.Row = 4;
                impactAnalysisLabel.Layout.Column = 1;
                impactAnalysisArea = uitextarea(problemGrid, ...
                    'Value', '此处设计正确实现了需求，但是需求只有启动电机逻辑，没有停止电机逻辑', ...
                    'BackgroundColor', colors.textAreaBg, 'FontColor', colors.inputText);
                impactAnalysisArea.Layout.Row = 4;
                impactAnalysisArea.Layout.Column = 2;
                
                % ========== 分组3：分配和审批（2个必填字段） ==========
                assignPanel = uipanel(mainGrid, 'Title', '★ 分配和审批（必填）', ...
                    'FontWeight', 'bold', 'FontSize', 11, ...
                    'BackgroundColor', colors.dialogBg, ...
                    'ForegroundColor', colors.labelText);
                assignPanel.Layout.Row = 3;
                assignPanel.Layout.Column = 1;
                
                assignGrid = uigridlayout(assignPanel, [2, 2]);
                assignGrid.BackgroundColor = colors.dialogBg;
                assignGrid.RowHeight = {'fit', 'fit'};
                assignGrid.ColumnWidth = {150, '1x'};
                assignGrid.Padding = [10, 10, 10, 10];
                assignGrid.RowSpacing = 8;
                assignGrid.ColumnSpacing = 10;
                
                % 分配人员（assignee，从用户列表中选择）
                assigneeLabel = uilabel(assignGrid, 'Text', '* 分配人员:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2]);
                assigneeLabel.Layout.Row = 1;
                assigneeLabel.Layout.Column = 1;
                assigneeDropDown = uidropdown(assignGrid, ...
                    'Items', {'正在加载用户列表...'}, ...
                    'Value', '正在加载用户列表...', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                assigneeDropDown.Layout.Row = 1;
                assigneeDropDown.Layout.Column = 2;
                
                % 审批人（approver）
                approverLabel = uilabel(assignGrid, 'Text', '* 审批人:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2]);
                approverLabel.Layout.Row = 2;
                approverLabel.Layout.Column = 1;
                approverDropDown = uidropdown(assignGrid, ...
                    'Items', {'正在加载审批人列表...'}, ...
                    'Value', '正在加载审批人列表...', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                approverDropDown.Layout.Row = 2;
                approverDropDown.Layout.Column = 2;
                
                % ========== 分组4：可选字段（4个） - 带checkbox控制 ==========
                optionalPanel = uipanel(mainGrid, 'Title', '☆ 可选字段（勾选后发送）', ...
                    'FontWeight', 'bold', 'FontSize', 11, ...
                    'BackgroundColor', colors.dialogBg, ...
                    'ForegroundColor', colors.labelText);
                optionalPanel.Layout.Row = 4;
                optionalPanel.Layout.Column = 1;
                
                % 3列布局：checkbox | 标签 | 输入框
                optionalGrid = uigridlayout(optionalPanel, [4, 3]);
                optionalGrid.BackgroundColor = colors.dialogBg;
                optionalGrid.RowHeight = {'fit', 'fit', 'fit', 'fit'};
                optionalGrid.ColumnWidth = {30, 120, '1x'};
                optionalGrid.Padding = [10, 10, 10, 10];
                optionalGrid.RowSpacing = 8;
                optionalGrid.ColumnSpacing = 5;
                
                % 基线版本（baseline，根据项目动态加载）
                baselineCheckbox = uicheckbox(optionalGrid, 'Text', '', ...
                    'Value', false);  % 默认不选中
                baselineCheckbox.Layout.Row = 1;
                baselineCheckbox.Layout.Column = 1;
                
                baselineLabel = uilabel(optionalGrid, 'Text', '基线版本:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                baselineLabel.Layout.Row = 1;
                baselineLabel.Layout.Column = 2;
                
                baselineDropDown = uidropdown(optionalGrid, ...
                    'Items', {'请先选择项目'}, ...
                    'Value', '请先选择项目', ...
                    'Editable', 'on', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                baselineDropDown.Layout.Row = 1;
                baselineDropDown.Layout.Column = 3;
                
                % 验证情况概述（verificationStatus，多行文本）
                verificationCheckbox = uicheckbox(optionalGrid, 'Text', '', ...
                    'Value', false);  % 默认不选中
                verificationCheckbox.Layout.Row = 2;
                verificationCheckbox.Layout.Column = 1;
                
                verificationLabel = uilabel(optionalGrid, 'Text', '验证情况概述:', ...
                    'FontWeight', 'bold', 'VerticalAlignment', 'top', ...
                    'FontColor', colors.labelText);
                verificationLabel.Layout.Row = 2;
                verificationLabel.Layout.Column = 2;
                
                verificationArea = uitextarea(optionalGrid, ...
                    'Value', '', ...
                    'Placeholder', '在开发阶段评审（2025年3月26日），专家组分析修改后的模型，确认问题已经关闭。', ...
                    'BackgroundColor', colors.textAreaBg, 'FontColor', colors.inputText);
                verificationArea.Layout.Row = 2;
                verificationArea.Layout.Column = 3;
                
                % 确定严重度（confirmedSeverity，枚举：严重/一般）
                confirmedSeverityCheckbox = uicheckbox(optionalGrid, 'Text', '', ...
                    'Value', false);  % 默认不选中
                confirmedSeverityCheckbox.Layout.Row = 3;
                confirmedSeverityCheckbox.Layout.Column = 1;
                
                confirmedSeverityLabel = uilabel(optionalGrid, 'Text', '确定严重度:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                confirmedSeverityLabel.Layout.Row = 3;
                confirmedSeverityLabel.Layout.Column = 2;
                
                confirmedSeverityDropDown = uidropdown(optionalGrid, ...
                    'Items', {'', '严重', '一般'}, ...
                    'Value', '', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                confirmedSeverityDropDown.Layout.Row = 3;
                confirmedSeverityDropDown.Layout.Column = 3;
                
                % 处置计划（disposalPlan，多行文本）
                disposalPlanCheckbox = uicheckbox(optionalGrid, 'Text', '', ...
                    'Value', false);  % 默认不选中
                disposalPlanCheckbox.Layout.Row = 4;
                disposalPlanCheckbox.Layout.Column = 1;
                
                disposalPlanLabel = uilabel(optionalGrid, 'Text', '处置计划:', ...
                    'FontWeight', 'bold', 'VerticalAlignment', 'top', ...
                    'FontColor', colors.labelText);
                disposalPlanLabel.Layout.Row = 4;
                disposalPlanLabel.Layout.Column = 2;
                
                disposalPlanArea = uitextarea(optionalGrid, ...
                    'Value', '', ...
                    'Placeholder', '由于不影响实际输出，与客户沟通后不修改需求，但是按照正确实现修改。提交变更申请，在下一个版本发布。', ...
                    'BackgroundColor', colors.textAreaBg, 'FontColor', colors.inputText);
                disposalPlanArea.Layout.Row = 4;
                disposalPlanArea.Layout.Column = 3;
                
                % ========== 第5行：按钮区域（单行布局：左侧操作按钮，右侧提交/取消）==========
                buttonPanel = uipanel(mainGrid, 'BorderType', 'none', ...
                    'BackgroundColor', colors.dialogBg);
                buttonPanel.Layout.Row = 5;
                buttonPanel.Layout.Column = 1;
                
                buttonGrid = uigridlayout(buttonPanel, [1, 6]);
                buttonGrid.RowHeight = {'fit'};
                buttonGrid.ColumnWidth = {120, 120, 120, '1x', 120, 120};
                buttonGrid.Padding = [0, 10, 0, 10];
                buttonGrid.ColumnSpacing = 10;
                
                % 左侧按钮组
                % 导出JSON按钮
                exportJsonBtn = uibutton(buttonGrid, 'push', ...
                    'Text', '💾 暂存JSON', ...
                    'BackgroundColor', [0.3 0.5 0.7], ...
                    'FontColor', colors.btnText, ...
                    'FontWeight', 'bold');
                exportJsonBtn.Layout.Row = 1;
                exportJsonBtn.Layout.Column = 1;
                
                % 导出Markdown按钮
                exportMdBtn = uibutton(buttonGrid, 'push', ...
                    'Text', '📝 暂存MD', ...
                    'BackgroundColor', [0.5 0.4 0.6], ...
                    'FontColor', colors.btnText, ...
                    'FontWeight', 'bold');
                exportMdBtn.Layout.Row = 1;
                exportMdBtn.Layout.Column = 2;
                
                % 从JSON导入按钮
                importJsonBtn = uibutton(buttonGrid, 'push', ...
                    'Text', '📂 导入JSON', ...
                    'BackgroundColor', [0.6 0.5 0.3], ...
                    'FontColor', colors.btnText, ...
                    'FontWeight', 'bold');
                importJsonBtn.Layout.Row = 1;
                importJsonBtn.Layout.Column = 3;
                
                % 中间空白占位（填充剩余空间）
                emptyLabel = uilabel(buttonGrid, 'Text', '');
                emptyLabel.Layout.Row = 1;
                emptyLabel.Layout.Column = 4;
                
                % 右侧按钮组
                % 提交按钮
                submitBtn = uibutton(buttonGrid, 'push', ...
                    'Text', '✅ 提交PR', ...
                    'BackgroundColor', colors.submitBtn, ...
                    'FontColor', colors.btnText, ...
                    'FontWeight', 'bold');
                submitBtn.Layout.Row = 1;
                submitBtn.Layout.Column = 5;
                
                % 取消按钮
                cancelBtn = uibutton(buttonGrid, 'push', ...
                    'Text', '❌ 取消', ...
                    'BackgroundColor', colors.cancelBtn, ...
                    'FontColor', colors.btnText, ...
                    'FontWeight', 'bold');
                cancelBtn.Layout.Row = 1;
                cancelBtn.Layout.Column = 6;
                
                % 设置按钮回调函数 - 传递所有字段引用（包含可选字段的checkbox）
                submitBtn.ButtonPushedFcn = @(btn,event) app.submitPRFormV2(prDialog, ...
                    projectDropDown, issueNumberEdit, reporterDropDown, sourceEdit, initialSeverityDropDown, ...
                    problemDescArea, problemMechanismArea, relatedReqEdit, impactAnalysisArea, ...
                    assigneeDropDown, approverDropDown, ...
                    baselineDropDown, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                    baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox);
                
                cancelBtn.ButtonPushedFcn = @(btn,event) delete(prDialog);
                
                % 导出/导入按钮回调（包含checkbox）
                exportJsonBtn.ButtonPushedFcn = @(btn,event) app.exportPRToJSON(prDialog, ...
                    projectDropDown, issueNumberEdit, reporterDropDown, sourceEdit, initialSeverityDropDown, ...
                    problemDescArea, problemMechanismArea, relatedReqEdit, impactAnalysisArea, ...
                    assigneeDropDown, approverDropDown, ...
                    baselineDropDown, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                    baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox);
                
                exportMdBtn.ButtonPushedFcn = @(btn,event) app.exportPRToMarkdown(prDialog, ...
                    projectDropDown, issueNumberEdit, reporterDropDown, sourceEdit, initialSeverityDropDown, ...
                    problemDescArea, problemMechanismArea, relatedReqEdit, impactAnalysisArea, ...
                    assigneeDropDown, approverDropDown, ...
                    baselineDropDown, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                    baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox);
                
                importJsonBtn.ButtonPushedFcn = @(btn,event) app.importPRFromJSON(prDialog, ...
                    projectDropDown, issueNumberEdit, reporterDropDown, sourceEdit, initialSeverityDropDown, ...
                    problemDescArea, problemMechanismArea, relatedReqEdit, impactAnalysisArea, ...
                    assigneeDropDown, approverDropDown, ...
                    baselineDropDown, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                    baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox);
                
                % 设置对话框居中显示
                movegui(prDialog, 'center');
                
                % 异步加载项目列表（3从服务器动态加载）
                app.loadProjectsAsync(projectDropDown, baselineDropDown);
                
                % 设置项目变化时的回调：自动更新基线列表（级联）
                projectDropDown.ValueChangedFcn = @(dd, event) app.onProjectChanged(dd, baselineDropDown);
                
                % 异步加载用户列表（用于reporter和assignee）
                app.loadUsersAsync(reporterDropDown, assigneeDropDown);
                
                % 异步加载审批人列表
                app.loadApproversAsync(approverDropDown);
                
                % 确保主窗口保持前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('prDialog', 'var') && isvalid(prDialog)
                    delete(prDialog);
                end
                uialert(app.MainFigure, sprintf('创建PR表单对话框失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function createCRSubmissionDialog(app)
            % 创建CR提交表单对话框 - 符合API v2.0规格（4个必填+7个可选字段）
            try
                % 获取当前主题的颜色方案
                colors = app.getCurrentThemeColors();
                % 创建模态对话框窗口
                crDialog = uifigure('Name', '变更请求 (CR) 提交表单 - API v2.0', ...
                    'Position', [150, 100, 900, 700], ...
                    'Resize', 'on', ...
                    'WindowStyle', 'modal');
                % 对齐主界面原生主题（MATLAB 2025a）
                if isprop(crDialog, 'Theme') && isprop(app.MainFigure, 'Theme')
                    crDialog.Theme = app.MainFigure.Theme;
                    crDialog.ThemeMode = "manual";
                end
                % 应用品牌浅/深主题背景色
                crDialog.Color = colors.dialogBg;
                
                % 确保对话框位于最上层（DO-178第8条规则：窗口层级管理）
                figure(crDialog);
                
                % 创建主网格布局（包含2个分组面板+按钮区域，共3行）
                mainGrid = uigridlayout(crDialog, [3, 1]);
                mainGrid.BackgroundColor = colors.dialogBg;
                mainGrid.RowHeight = {'fit', 'fit', 'fit'};
                mainGrid.ColumnWidth = {'1x'};
                mainGrid.Padding = [15, 15, 15, 15];
                mainGrid.RowSpacing = 10;
                
                % ========== 分组1：必填字段（4个） ==========
                requiredPanel = uipanel(mainGrid, 'Title', '★ 必填字段', ...
                    'FontWeight', 'bold', 'FontSize', 11, ...
                    'BackgroundColor', colors.dialogBg, ...
                    'ForegroundColor', colors.labelText);
                requiredPanel.Layout.Row = 1;
                requiredPanel.Layout.Column = 1;
                
                requiredGrid = uigridlayout(requiredPanel, [4, 2]);
                requiredGrid.BackgroundColor = colors.dialogBg;
                requiredGrid.RowHeight = {'fit', 'fit', 'fit', '1x'};
                requiredGrid.ColumnWidth = {150, '1x'};
                requiredGrid.Padding = [10, 10, 10, 10];
                requiredGrid.RowSpacing = 8;
                requiredGrid.ColumnSpacing = 10;
                
                % 变更申请号（ticketNumber）
                ticketNumberLabel = uilabel(requiredGrid, 'Text', '* 变更申请号:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2]);
                ticketNumberLabel.Layout.Row = 1;
                ticketNumberLabel.Layout.Column = 1;
                ticketNumberEdit = uieditfield(requiredGrid, 'text', ...
                    'Value', 'E-RAS-IVOS-CR-001', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                ticketNumberEdit.Layout.Row = 1;
                ticketNumberEdit.Layout.Column = 2;
                
                % 提交人（reporter）
                reporterLabel = uilabel(requiredGrid, 'Text', '* 提交人:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2]);
                reporterLabel.Layout.Row = 2;
                reporterLabel.Layout.Column = 1;
                reporterDropDown = uidropdown(requiredGrid, ...
                    'Items', {'正在加载用户列表...'}, ...
                    'Value', '正在加载用户列表...', ...
                    'Editable', 'on', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                reporterDropDown.Layout.Row = 2;
                reporterDropDown.Layout.Column = 2;
                
                % 审批人（approver）
                approverLabel = uilabel(requiredGrid, 'Text', '* 审批人:', ...
                    'FontWeight', 'bold', 'FontColor', [0.8 0.2 0.2]);
                approverLabel.Layout.Row = 3;
                approverLabel.Layout.Column = 1;
                approverDropDown = uidropdown(requiredGrid, ...
                    'Items', {'正在加载审批人列表...'}, ...
                    'Value', '正在加载审批人列表...', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                approverDropDown.Layout.Row = 3;
                approverDropDown.Layout.Column = 2;
                
                % 变更描述（changeDescription，多行文本）
                changeDescLabel = uilabel(requiredGrid, 'Text', '* 变更描述:', ...
                    'FontWeight', 'bold', 'VerticalAlignment', 'top', ...
                    'FontColor', [0.8 0.2 0.2]);
                changeDescLabel.Layout.Row = 4;
                changeDescLabel.Layout.Column = 1;
                changeDescArea = uitextarea(requiredGrid, ...
                    'Value', '系统架构重构需求，优化数据处理性能', ...
                    'BackgroundColor', colors.textAreaBg, 'FontColor', colors.inputText);
                changeDescArea.Layout.Row = 4;
                changeDescArea.Layout.Column = 2;
                
                % ========== 分组2：可选字段（7个） - 带checkbox控制 ==========
                optionalPanel = uipanel(mainGrid, 'Title', '☆ 可选字段（勾选后发送）', ...
                    'FontWeight', 'bold', 'FontSize', 11, ...
                    'BackgroundColor', colors.dialogBg, ...
                    'ForegroundColor', colors.labelText);
                optionalPanel.Layout.Row = 2;
                optionalPanel.Layout.Column = 1;
                
                % 3列布局：checkbox | 标签 | 输入框
                optionalGrid = uigridlayout(optionalPanel, [7, 3]);
                optionalGrid.BackgroundColor = colors.dialogBg;
                optionalGrid.RowHeight = {'fit', 'fit', 'fit', 'fit', 'fit', 'fit', 'fit'};
                optionalGrid.ColumnWidth = {30, 120, '1x'};
                optionalGrid.Padding = [10, 10, 10, 10];
                optionalGrid.RowSpacing = 8;
                optionalGrid.ColumnSpacing = 5;
                
                % 1. 项目名称
                projectCheckbox = uicheckbox(optionalGrid, 'Text', '', 'Value', false);
                projectCheckbox.Layout.Row = 1;
                projectCheckbox.Layout.Column = 1;
                
                projectLabel = uilabel(optionalGrid, 'Text', '项目名称:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                projectLabel.Layout.Row = 1;
                projectLabel.Layout.Column = 2;
                
                projectEdit = uieditfield(optionalGrid, 'text', ...
                    'Value', '', ...
                    'Placeholder', 'LB200-U IVOS软件', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                projectEdit.Layout.Row = 1;
                projectEdit.Layout.Column = 3;
                
                % 2. 分配人员
                assigneeCheckbox = uicheckbox(optionalGrid, 'Text', '', 'Value', false);
                assigneeCheckbox.Layout.Row = 2;
                assigneeCheckbox.Layout.Column = 1;
                
                assigneeLabel = uilabel(optionalGrid, 'Text', '分配人员:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                assigneeLabel.Layout.Row = 2;
                assigneeLabel.Layout.Column = 2;
                
                assigneeEdit = uieditfield(optionalGrid, 'text', ...
                    'Value', '', ...
                    'Placeholder', 'tech_lead', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                assigneeEdit.Layout.Row = 2;
                assigneeEdit.Layout.Column = 3;
                
                % 3. 报告日期（reportDate，YYYY-MM-DD格式）
                reportDateCheckbox = uicheckbox(optionalGrid, 'Text', '', 'Value', false);
                reportDateCheckbox.Layout.Row = 3;
                reportDateCheckbox.Layout.Column = 1;
                
                reportDateLabel = uilabel(optionalGrid, 'Text', '报告日期:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                reportDateLabel.Layout.Row = 3;
                reportDateLabel.Layout.Column = 2;
                
                reportDatePicker = uidatepicker(optionalGrid, 'Value', datetime('today'));
                reportDatePicker.Layout.Row = 3;
                reportDatePicker.Layout.Column = 3;
                
                % 4. 变更来源
                changeSourceCheckbox = uicheckbox(optionalGrid, 'Text', '', 'Value', false);
                changeSourceCheckbox.Layout.Row = 4;
                changeSourceCheckbox.Layout.Column = 1;
                
                changeSourceLabel = uilabel(optionalGrid, 'Text', '变更来源:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                changeSourceLabel.Layout.Row = 4;
                changeSourceLabel.Layout.Column = 2;
                
                changeSourceEdit = uieditfield(optionalGrid, 'text', ...
                    'Value', '', ...
                    'Placeholder', '如：工程开发/测试阶段/用户需求变更', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                changeSourceEdit.Layout.Row = 4;
                changeSourceEdit.Layout.Column = 3;
                
                % 5. 基线版本
                baselineCheckbox = uicheckbox(optionalGrid, 'Text', '', 'Value', false);
                baselineCheckbox.Layout.Row = 5;
                baselineCheckbox.Layout.Column = 1;
                
                baselineLabel = uilabel(optionalGrid, 'Text', '基线版本:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                baselineLabel.Layout.Row = 5;
                baselineLabel.Layout.Column = 2;
                
                baselineEdit = uieditfield(optionalGrid, 'text', ...
                    'Value', '', ...
                    'Placeholder', 'baseline-LLR-001 2.0', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                baselineEdit.Layout.Row = 5;
                baselineEdit.Layout.Column = 3;
                
                % 6. 举一反三分析
                analysisCheckbox = uicheckbox(optionalGrid, 'Text', '', 'Value', false);
                analysisCheckbox.Layout.Row = 6;
                analysisCheckbox.Layout.Column = 1;
                
                analysisLabel = uilabel(optionalGrid, 'Text', '举一反三分析:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                analysisLabel.Layout.Row = 6;
                analysisLabel.Layout.Column = 2;
                
                analysisEdit = uieditfield(optionalGrid, 'text', ...
                    'Value', '', ...
                    'Placeholder', '举一反三分析内容', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                analysisEdit.Layout.Row = 6;
                analysisEdit.Layout.Column = 3;
                
                % 7. 关联PR的ID
                relatedPRCheckbox = uicheckbox(optionalGrid, 'Text', '', 'Value', false);
                relatedPRCheckbox.Layout.Row = 7;
                relatedPRCheckbox.Layout.Column = 1;
                
                relatedPRLabel = uilabel(optionalGrid, 'Text', '关联PR ID:', ...
                    'FontWeight', 'bold', 'FontColor', colors.labelText);
                relatedPRLabel.Layout.Row = 7;
                relatedPRLabel.Layout.Column = 2;
                
                relatedPREdit = uieditfield(optionalGrid, 'numeric', ...
                    'Value', 0, ...
                    'Limits', [0 Inf], ...
                    'RoundFractionalValues', 'on', ...
                    'BackgroundColor', colors.inputBg, 'FontColor', colors.inputText);
                relatedPREdit.Layout.Row = 7;
                relatedPREdit.Layout.Column = 3;
                
                % ========== 第3行：按钮区域（单行布局：左侧操作按钮，右侧提交/取消）==========
                buttonPanel = uipanel(mainGrid, 'BorderType', 'none', ...
                    'BackgroundColor', colors.dialogBg);
                buttonPanel.Layout.Row = 3;
                buttonPanel.Layout.Column = 1;
                
                buttonGrid = uigridlayout(buttonPanel, [1, 6]);
                buttonGrid.RowHeight = {'fit'};
                buttonGrid.ColumnWidth = {120, 120, 120, '1x', 120, 120};
                buttonGrid.Padding = [0, 10, 0, 10];
                buttonGrid.ColumnSpacing = 10;
                
                % 左侧按钮组
                % 导出JSON按钮
                exportJsonBtn = uibutton(buttonGrid, 'push', ...
                    'Text', '💾 暂存JSON', ...
                    'BackgroundColor', [0.3 0.5 0.7], ...
                    'FontColor', colors.btnText, ...
                    'FontWeight', 'bold');
                exportJsonBtn.Layout.Row = 1;
                exportJsonBtn.Layout.Column = 1;
                
                % 导出Markdown按钮
                exportMdBtn = uibutton(buttonGrid, 'push', ...
                    'Text', '📝 暂存MD', ...
                    'BackgroundColor', [0.5 0.4 0.6], ...
                    'FontColor', colors.btnText, ...
                    'FontWeight', 'bold');
                exportMdBtn.Layout.Row = 1;
                exportMdBtn.Layout.Column = 2;
                
                % 从JSON导入按钮
                importJsonBtn = uibutton(buttonGrid, 'push', ...
                    'Text', '📂 导入JSON', ...
                    'BackgroundColor', [0.6 0.5 0.3], ...
                    'FontColor', colors.btnText, ...
                    'FontWeight', 'bold');
                importJsonBtn.Layout.Row = 1;
                importJsonBtn.Layout.Column = 3;
                
                % 中间空白占位（填充剩余空间）
                emptyLabel = uilabel(buttonGrid, 'Text', '');
                emptyLabel.Layout.Row = 1;
                emptyLabel.Layout.Column = 4;
                
                % 右侧按钮组
                % 提交按钮
                submitBtn = uibutton(buttonGrid, 'push', ...
                    'Text', '✅ 提交CR', ...
                    'BackgroundColor', colors.submitBtn, ...
                    'FontColor', colors.btnText, ...
                    'FontWeight', 'bold');
                submitBtn.Layout.Row = 1;
                submitBtn.Layout.Column = 5;
                
                % 取消按钮
                cancelBtn = uibutton(buttonGrid, 'push', ...
                    'Text', '❌ 取消', ...
                    'BackgroundColor', colors.cancelBtn, ...
                    'FontColor', colors.btnText, ...
                    'FontWeight', 'bold');
                cancelBtn.Layout.Row = 1;
                cancelBtn.Layout.Column = 6;
                
                % 设置按钮回调函数 - 传递所有字段的引用（包含可选字段checkbox）
                submitBtn.ButtonPushedFcn = @(btn,event) app.submitCRForm(crDialog, ...
                    ticketNumberEdit, reporterDropDown, approverDropDown, changeDescArea, ...
                    projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                    baselineEdit, analysisEdit, relatedPREdit, ...
                    projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                    baselineCheckbox, analysisCheckbox, relatedPRCheckbox);
                
                cancelBtn.ButtonPushedFcn = @(btn,event) delete(crDialog);
                
                % 导出/导入按钮回调（包含checkbox）
                exportJsonBtn.ButtonPushedFcn = @(btn,event) app.exportCRToJSON(crDialog, ...
                    ticketNumberEdit, reporterDropDown, approverDropDown, changeDescArea, ...
                    projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                    baselineEdit, analysisEdit, relatedPREdit, ...
                    projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                    baselineCheckbox, analysisCheckbox, relatedPRCheckbox);
                
                exportMdBtn.ButtonPushedFcn = @(btn,event) app.exportCRToMarkdown(crDialog, ...
                    ticketNumberEdit, reporterDropDown, approverDropDown, changeDescArea, ...
                    projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                    baselineEdit, analysisEdit, relatedPREdit, ...
                    projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                    baselineCheckbox, analysisCheckbox, relatedPRCheckbox);
                
                importJsonBtn.ButtonPushedFcn = @(btn,event) app.importCRFromJSON(crDialog, ...
                    ticketNumberEdit, reporterDropDown, approverDropDown, changeDescArea, ...
                    projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                    baselineEdit, analysisEdit, relatedPREdit, ...
                    projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                    baselineCheckbox, analysisCheckbox, relatedPRCheckbox);
                
                % 设置对话框居中显示
                movegui(crDialog, 'center');
                
                % 异步加载审批人列表
                app.loadApproversAsync(approverDropDown);
                
                % 异步加载用户列表并设置Reporter默认值
                app.loadUsersAndSetReporter(reporterDropDown);
                
                % 确保主窗口保持前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('crDialog', 'var') && isvalid(crDialog)
                    delete(crDialog);
                end
                uialert(app.MainFigure, sprintf('创建CR表单对话框失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        function submitCRForm(app, dialogFig, ticketNumberEdit, reporterDropDown, approverDropDown, ...
                changeDescArea, projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                baselineEdit, analysisEdit, relatedPREdit, ...
                projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                baselineCheckbox, analysisCheckbox, relatedPRCheckbox)
            % 处理CR表单提交 - API v2.0版本（4个必填+7个可选字段）
            % 2明确错误处理，不使用fallback机制
            % 可选字段由checkbox控制是否发送
            try
                % ========== 验证必填字段（4个） ==========
                
                % 1. 验证变更申请号
                if isempty(strtrim(ticketNumberEdit.Value))
                    uialert(dialogFig, '变更申请号不能为空！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 2. 验证提交人
                reporterValue = reporterDropDown.Value;
                if isempty(reporterValue) || strcmp(reporterValue, '正在加载用户列表...') || ...
                   strcmp(reporterValue, '获取用户列表失败') || strcmp(reporterValue, '加载失败-请手动输入')
                    uialert(dialogFig, '请选择或输入有效的提交人', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 3. 验证审批人
                approverValue = approverDropDown.Value;
                if isempty(approverValue) || strcmp(approverValue, '正在加载审批人列表...')
                    uialert(dialogFig, '请选择审批人！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 4. 验证变更描述
                changeDesc = changeDescArea.Value;
                if iscell(changeDesc)
                    changeDesc = strjoin(changeDesc, newline);
                end
                if isempty(strtrim(changeDesc))
                    uialert(dialogFig, '变更描述不能为空！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % ========== 收集表单数据 - 严格按照API v2.0规格 ==========
                crData = struct();
                
                % 必填字段（4个）
                crData.ticketNumber = strtrim(ticketNumberEdit.Value);
                crData.reporter = strtrim(reporterValue);
                crData.approver = strtrim(approverValue);
                crData.changeDescription = changeDesc;  % 多行文本
                
                % 可选字段（7个）- 只有checkbox选中时才添加
                % 基于项目编码规则：避免发送空内容或非主动发送的可选项
                
                % 1. project（项目名称）
                if projectCheckbox.Value  % checkbox选中
                projectValue = strtrim(projectEdit.Value);
                if ~isempty(projectValue)
                    crData.project = projectValue;
                    end
                end
                
                % 2. assignee（分配人员）
                if assigneeCheckbox.Value  % checkbox选中
                assigneeValue = strtrim(assigneeEdit.Value);
                if ~isempty(assigneeValue)
                    crData.assignee = assigneeValue;
                    end
                end
                
                % 3. reportDate（报告日期） - 格式化为YYYY-MM-DD
                if reportDateCheckbox.Value  % checkbox选中
                if ~isnat(reportDatePicker.Value)
                    crData.reportDate = char(reportDatePicker.Value, 'yyyy-MM-dd');
                    end
                end
                
                % 4. changeSource（变更来源）
                if changeSourceCheckbox.Value  % checkbox选中
                changeSourceValue = strtrim(changeSourceEdit.Value);
                if ~isempty(changeSourceValue)
                    crData.changeSource = changeSourceValue;
                    end
                end
                
                % 5. baseline（基线版本）
                if baselineCheckbox.Value  % checkbox选中
                baselineValue = strtrim(baselineEdit.Value);
                if ~isempty(baselineValue)
                    crData.baseline = baselineValue;
                    end
                end
                
                % 6. analysis（举一反三分析）
                if analysisCheckbox.Value  % checkbox选中
                analysisValue = strtrim(analysisEdit.Value);
                if ~isempty(analysisValue)
                    crData.analysis = analysisValue;
                    end
                end
                
                % 7. related_pr_id（关联PR的ID） - 只有大于0时才添加
                if relatedPRCheckbox.Value  % checkbox选中
                if relatedPREdit.Value > 0
                    crData.related_pr_id = relatedPREdit.Value;
                    end
                end
                
                % ⚠️ 重要：根据API规格，创建CR时禁止包含以下字段
                % approval, approvalComment, approvalTime - 系统自动处理
                % 违反此限制将返回错误码 CR_017
                
                % 显示进度条
                d = uiprogressdlg(dialogFig, 'Title', '提交CR', ...
                    'Message', '正在提交CR到ALM云端服务 (API v2.0)...', 'Indeterminate', 'on');
                
                % 使用预初始化的ALM服务提交到云端
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                [success, result] = app.ALMService.submitCR(crData);
                
                close(d);
                
                if success
                    % 提交成功
                    % CR使用ticketNumber而不是issueNumber
                    crNumber = '';
                    if isfield(result, 'ticketNumber')
                        crNumber = result.ticketNumber;
                    elseif isfield(result, 'ticket_number')
                        crNumber = result.ticket_number;
                    end
                    successMsg = sprintf('✅ %s\n\n详细信息：\n• CR编号：%s\n• 提交时间：%s', ...
                        result.message, crNumber, result.submitTime);
                    uialert(dialogFig, successMsg, 'CR提交成功', 'Icon', 'success');
                    
                    % 更新主界面消息区域
                    if isvalid(app.MessagesTextArea)
                        app.MessagesTextArea.Value = sprintf('✅ CR提交成功！编号: %s, 服务器ID: %d', ...
                            crNumber, result.cr_id);
                    end
                    
                    % 关闭对话框
                    delete(dialogFig);
                    
                    % 刷新CR表格数据
                    app.onRefreshPRCRButtonPushed();
                    
                    % 确保主窗口回到前台
                    % figure(app.MainFigure) #tmpfix;
                else
                    % 提交失败时不关闭对话框，让用户可以重新尝试
                    uialert(dialogFig, sprintf('❌ %s', result.error), 'CR提交失败', 'Icon', 'error');
                    
                    % 更新主界面消息区域
                    if isvalid(app.MessagesTextArea)
                        app.MessagesTextArea.Value = sprintf('❌ CR提交失败: %s', result.error);
                    end
                end
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                uialert(dialogFig, sprintf('提交CR失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function submitPRFormV2(app, dialogFig, projectDropDown, issueNumberEdit, reporterDropDown, ...
                sourceEdit, initialSeverityDropDown, ...
                problemDescArea, problemMechanismArea, relatedReqEdit, impactAnalysisArea, ...
                assigneeDropDown, approverDropDown, ...
                baselineDropDown, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox)
            % 处理PR表单提交 - API v2.0版本（15个字段：11必填+4可选）
            % 2明确错误处理，不使用fallback机制
            % 6不使用try-catch掩盖错误，便于调试
            % 可选字段由checkbox控制是否发送
            try
                % ========== 验证必填字段（11个） ==========
                
                % 1. 验证项目名称
                projectValue = projectDropDown.Value;
                if isempty(projectValue) || contains(projectValue, '加载') || contains(projectValue, '失败')
                    uialert(dialogFig, '请选择有效的项目名称！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 2. 验证问题编号
                if isempty(strtrim(issueNumberEdit.Value))
                    uialert(dialogFig, '问题编号不能为空！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 3. 验证提交人
                reporterValue = reporterDropDown.Value;
                if isempty(reporterValue) || strcmp(reporterValue, '正在加载用户列表...') || ...
                   strcmp(reporterValue, '获取用户列表失败') || strcmp(reporterValue, '加载失败-请手动输入')
                    uialert(dialogFig, '请选择或输入有效的提交人', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 4. 验证问题来源
                if isempty(strtrim(sourceEdit.Value))
                    uialert(dialogFig, '问题来源不能为空！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 5. 验证初定严重度（枚举字段，uidropdown保证有值）
                
                % 6. 验证问题描述
                problemDesc = problemDescArea.Value;
                if iscell(problemDesc)
                    problemDesc = strjoin(problemDesc, newline);
                end
                if isempty(strtrim(problemDesc))
                    uialert(dialogFig, '问题描述不能为空！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 7. 验证问题机理
                problemMech = problemMechanismArea.Value;
                if iscell(problemMech)
                    problemMech = strjoin(problemMech, newline);
                end
                if isempty(strtrim(problemMech))
                    uialert(dialogFig, '问题机理和复现情况不能为空！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 8. 验证关联需求
                if isempty(strtrim(relatedReqEdit.Value))
                    uialert(dialogFig, '关联需求不能为空！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 9. 验证影响性分析
                impactAnalysis = impactAnalysisArea.Value;
                if iscell(impactAnalysis)
                    impactAnalysis = strjoin(impactAnalysis, newline);
                end
                if isempty(strtrim(impactAnalysis))
                    uialert(dialogFig, '影响性分析不能为空！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 10. 验证分配人员
                assigneeValue = assigneeDropDown.Value;
                if isempty(assigneeValue) || contains(assigneeValue, '加载') || contains(assigneeValue, '失败')
                    uialert(dialogFig, '请选择有效的分配人员！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 11. 验证审批人
                approverValue = approverDropDown.Value;
                if isempty(approverValue) || strcmp(approverValue, '正在加载审批人列表...')
                    uialert(dialogFig, '请选择审批人！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % ========== 收集表单数据 - 严格按照API v2.0规格 ==========
                prData = struct();
                
                % 必填字段（11个）
                prData.project = strtrim(projectValue);
                prData.issueNumber = strtrim(issueNumberEdit.Value);
                prData.reporter = strtrim(reporterValue);
                prData.source = strtrim(sourceEdit.Value);  % 字符串，无枚举限制
                prData.initialSeverity = initialSeverityDropDown.Value;  % 枚举：严重/一般
                prData.problemDescription = problemDesc;  % 多行文本
                prData.problemMechanism = problemMech;  % 多行文本
                prData.relatedRequirements = strtrim(relatedReqEdit.Value);
                prData.impactAnalysis = impactAnalysis;  % 多行文本
                prData.assignee = strtrim(assigneeValue);
                prData.approver = strtrim(approverValue);
                
                % 可选字段（4个）- 只有checkbox选中时才添加
                % 基于项目编码规则：避免发送空内容或非主动发送的可选项
                
                % 1. baseline（基线版本）
                if baselineCheckbox.Value  % checkbox选中
                    baselineValue = baselineDropDown.Value;
                    % 过滤无效值
                    if ~isempty(baselineValue) && ~contains(baselineValue, '请先选择') && ...
                       ~contains(baselineValue, '加载') && ~contains(baselineValue, '失败') && ...
                       ~strcmp(baselineValue, '（可选）留空')
                        prData.baseline = strtrim(baselineValue);
                    end
                end
                
                % 2. verificationStatus（验证情况概述）
                if verificationCheckbox.Value  % checkbox选中
                verificationValue = verificationArea.Value;
                if iscell(verificationValue)
                    verificationValue = strjoin(verificationValue, newline);
                end
                    verificationValue = strtrim(verificationValue);
                    if ~isempty(verificationValue)
                    prData.verificationStatus = verificationValue;
                    end
                end
                
                % 3. confirmedSeverity（确定严重度）
                if confirmedSeverityCheckbox.Value  % checkbox选中
                confirmedSevValue = confirmedSeverityDropDown.Value;
                if ~isempty(confirmedSevValue)
                    prData.confirmedSeverity = confirmedSevValue;
                    end
                end
                
                % 4. disposalPlan（处置计划）
                if disposalPlanCheckbox.Value  % checkbox选中
                disposalValue = disposalPlanArea.Value;
                if iscell(disposalValue)
                    disposalValue = strjoin(disposalValue, newline);
                end
                    disposalValue = strtrim(disposalValue);
                    if ~isempty(disposalValue)
                    prData.disposalPlan = disposalValue;
                    end
                end
                
                % ⚠️ 重要：根据API规格，创建PR时禁止包含以下字段
                % approval, approvalComment, approvalTime - 系统自动处理
                % 违反此限制将返回错误码 PR_017
                
                % 显示进度条
                d = uiprogressdlg(dialogFig, 'Title', '提交PR', ...
                    'Message', '正在提交PR到ALM云端服务 (API v2.0)...', 'Indeterminate', 'on');
                
                % 使用预初始化的ALM服务提交到云端
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                [success, result] = app.ALMService.submitPR(prData);
                
                close(d);
                
                if success
                    % 提交成功
                    successMsg = sprintf('✅ %s\n\n详细信息：\n• PR编号：%s\n• 提交时间：%s', ...
                        result.message, result.issueNumber, result.submitTime);
                    uialert(dialogFig, successMsg, 'PR提交成功', 'Icon', 'success');
                    
                    % 更新主界面消息区域
                    if isvalid(app.MessagesTextArea)
                        app.MessagesTextArea.Value = sprintf('✅ PR提交成功！编号: %s, 服务器ID: %d', ...
                            result.issueNumber, result.pr_id);
                    end
                    
                    % 关闭对话框
                    delete(dialogFig);
                    
                    % 刷新PR表格数据
                    app.onRefreshPRCRButtonPushed();
                    
                    % 确保主窗口回到前台
                    % figure(app.MainFigure) #tmpfix;
                else
                    % 提交失败时不关闭对话框，让用户可以重新尝试
                    uialert(dialogFig, sprintf('❌ %s', result.error), 'PR提交失败', 'Icon', 'error');
                    
                    % 更新主界面消息区域
                    if isvalid(app.MessagesTextArea)
                        app.MessagesTextArea.Value = sprintf('❌ PR提交失败: %s', result.error);
                    end
                end
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                uialert(dialogFig, sprintf('提交PR失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function submitPRForm(app, dialogFig, changeRequestEdit, assigneeEdit, ...
                reporterDropDown, changeDescArea, verificationArea, analysisArea, ...
                baselineEdit, approverDropDown, datePicker)
            % 处理PR表单提交 - 旧版本（已废弃，保留用于向下兼容）
            % 新代码请使用 submitPRFormV2
            try
                % 验证Reporter字段
                reporterValue = reporterDropDown.Value;
                if isempty(reporterValue) || strcmp(reporterValue, '正在加载用户列表...') || ...
                   strcmp(reporterValue, '获取用户列表失败') || strcmp(reporterValue, '加载失败-请手动输入')
                    uialert(dialogFig, '请选择或输入有效的提报人', '验证错误', 'Icon', 'warning');
                    return;
                end
                
                % 如果成功获取了用户列表，验证reporter是否在列表中
                if ~isempty(reporterDropDown.Items) && ~any(strcmp(reporterDropDown.Items, {'正在加载用户列表...', '获取用户列表失败', '加载失败-请手动输入'}))
                    if ~any(strcmp(reporterDropDown.Items, reporterValue))
                        % Reporter不在有效列表中，但允许继续（因为可以手动输入）
                        answer = questdlg(sprintf('提报人"%s"不在系统用户列表中，是否继续提交？', reporterValue), ...
                            '确认提交', '继续', '取消', '取消');
                        if strcmp(answer, '取消')
                            return;
                        end
                    end
                end
                
                % 收集表单数据并转换为ALM API格式 - 严格按照API规格
                prData = struct();
                
                % 根据最新数据格式规格收集PR数据 - 11个核心字段
                prData.issueNumber = changeRequestEdit.Value;
                prData.reporter = reporterDropDown.Value;  % 使用验证过的Reporter字段
                prData.assignee = assigneeEdit.Value;      % 分配人员（必填字段）
                
                % changeDescription - 确保为数组格式
                if iscell(changeDescArea.Value)
                    prData.changeDescription = changeDescArea.Value;
                else
                    prData.changeDescription = {changeDescArea.Value};
                end
                
                % verificationStatus - 确保为数组格式
                if iscell(verificationArea.Value)
                    prData.verificationStatus = verificationArea.Value;
                else
                    prData.verificationStatus = {verificationArea.Value};
                end
                
                % analysis - 可选字段，确保为数组格式
                if iscell(analysisArea.Value)
                    prData.analysis = analysisArea.Value;
                else
                    prData.analysis = {analysisArea.Value};
                end
                
                prData.baseline = baselineEdit.Value;
                prData.approver = approverDropDown.Value;
                
                % ⚠️ 重要：根据API规格，创建PR时禁止包含以下字段
                % approval, approvalComment, approvalTime - 系统自动处理
                % 违反此限制将返回错误码 PR_017
                
                % 简单数据验证
                if isempty(strtrim(prData.issueNumber))
                    uialert(dialogFig, '变更申请号不能为空！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                if isempty(strtrim(prData.assignee))
                    uialert(dialogFig, '分配人员不能为空！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                if isempty(strtrim(prData.changeDescription{1}))
                    uialert(dialogFig, '变更方案概述不能为空！', '表单验证', 'Icon', 'warning');
                    return;
                end
                
                % 显示进度条
                d = uiprogressdlg(dialogFig, 'Title', '提交PR', ...
                    'Message', '正在提交PR到ALM云端服务...', 'Indeterminate', 'on');
                
                % 使用预初始化的ALM服务提交到云端
                % 如果服务未初始化，则创建新实例
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                [success, result] = app.ALMService.submitPR(prData);
                
                close(d);
                
                if success
                    % 提交成功
                    successMsg = sprintf('✅ %s\n\n详细信息：\n• PR编号：%s\n• 提交时间：%s', ...
                        result.message, result.issueNumber, result.submitTime);
                    uialert(dialogFig, successMsg, 'PR提交成功', 'Icon', 'success');
                    
                    % 更新主界面消息区域
                    if isvalid(app.MessagesTextArea)
                        app.MessagesTextArea.Value = sprintf('✅ PR提交成功！编号: %s, 服务器ID: %d', ...
                            result.issueNumber, result.pr_id);
                    end
                    
                    % 关闭对话框
                    delete(dialogFig);
                    
                    % 刷新PR表格数据
                    app.onRefreshPRCRButtonPushed();
                    
                    % 确保主窗口回到前台
                    % figure(app.MainFigure) #tmpfix;
                else
                    % 提交失败时不关闭对话框，让用户可以重新尝试
                    uialert(dialogFig, sprintf('❌ %s', result.error), 'PR提交失败', 'Icon', 'error');
                    
                    % 更新主界面消息区域
                    if isvalid(app.MessagesTextArea)
                        app.MessagesTextArea.Value = sprintf('❌ PR提交失败: %s', result.error);
                    end
                end
                
            catch ME
                if exist('d', 'var')
                    close(d);
                end
                uialert(dialogFig, sprintf('提交PR失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        

        function colors = getCurrentThemeColors(app)
            % 获取当前主题颜色（方案B）：
            % 1) 优先读取MATLAB 2025a原生Theme（若存在）
            % 2) 回退到app.CurrentTheme（兼容旧版/手动主题）
            try
                isDark = false;
                if isprop(app.MainFigure, 'Theme')
                    themeVal = app.MainFigure.Theme;
                    % Theme 可能是字符串或对象（带BaseColorStyle）
                    if isobject(themeVal) && isprop(themeVal, 'BaseColorStyle')
                        isDark = themeVal.BaseColorStyle == "dark";
                    else
                        isDark = strcmp(string(themeVal), "dark");
                    end
                else
                    isDark = app.CurrentTheme == "dark";
                end
                
                if isDark
                    % 深色主题配色方案
                    colors = struct();
                    colors.dialogBg = [0.2, 0.2, 0.2];        % 对话框背景
                    colors.inputBg = [0.3, 0.3, 0.3];         % 输入框背景
                    colors.textAreaBg = [0.35, 0.35, 0.4];    % 文本区域背景  
                    colors.labelText = [1, 1, 1];             % 标签文字颜色（纯白色，清晰可读）
                    colors.inputText = [1, 1, 1];             % 输入框文字颜色（纯白色，清晰可读）
                    colors.submitBtn = [0.2, 0.6, 0.8];       % 提交按钮（蓝色）
                    colors.cancelBtn = [0.4, 0.4, 0.4];       % 取消按钮（深灰）
                    colors.btnText = [1, 1, 1];               % 按钮文字（白色）
                else
                    % 浅色主题配色方案 - 与主界面一致的简单浅色风格
                    colors = struct();
                    colors.dialogBg = [0.94, 0.94, 0.94];     % 对话框背景（浅灰色，与主界面一致）
                    colors.inputBg = [1, 1, 1];               % 输入框背景（纯白色）
                    colors.textAreaBg = [1, 1, 1];            % 文本区域背景（纯白色）
                    colors.labelText = [0.2, 0.2, 0.2];       % 标签文字颜色（深灰色）
                    colors.inputText = [0.2, 0.2, 0.2];       % 输入框文字颜色（深灰色）
                    colors.submitBtn = [0.2, 0.6, 0.8];       % 提交按钮（蓝色）
                    colors.cancelBtn = [0.6, 0.6, 0.6];       % 取消按钮（中性灰）
                    colors.btnText = [1, 1, 1];               % 按钮文字（白色）
                end
                
            catch ME
                % 默认使用浅色主题 - 与主界面一致的简单浅色风格
                colors = struct();
                colors.dialogBg = [0.94, 0.94, 0.94];     % 对话框背景（浅灰色）
                colors.inputBg = [1, 1, 1];               % 输入框背景（纯白色）
                colors.textAreaBg = [1, 1, 1];            % 文本区域背景（纯白色）
                colors.labelText = [0.2, 0.2, 0.2];       % 标签文字颜色（深灰色）
                colors.inputText = [0.2, 0.2, 0.2];       % 输入框文字颜色（深灰色）
                colors.submitBtn = [0.2, 0.6, 0.8];       % 提交按钮（蓝色）
                colors.cancelBtn = [0.6, 0.6, 0.6];       % 取消按钮（中性灰）
                colors.btnText = [1, 1, 1];               % 按钮文字（白色）
            end
        end
        
        %% PR v2.0 导出/导入函数（遵循第4条规则：调用PRExportImportService）
        function exportPRToJSON(app, dialogFig, projectEdit, issueNumberEdit, reporterDropDown, ...
                sourceEdit, initialSeverityDropDown, problemDescArea, problemMechanismArea, ...
                relatedReqEdit, impactAnalysisArea, assigneeEdit, approverDropDown, ...
                baselineEdit, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox)
            % 导出PR v2.0到JSON文件（包含checkbox状态）
            try
                % 收集表单数据（15个字段 + 4个checkbox状态）
                prData = app.collectPRFormData(projectEdit, issueNumberEdit, reporterDropDown, ...
                    sourceEdit, initialSeverityDropDown, problemDescArea, problemMechanismArea, ...
                    relatedReqEdit, impactAnalysisArea, assigneeEdit, approverDropDown, ...
                    baselineEdit, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                    baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox);
                
                % 显示文件保存对话框
                [filename, pathname] = uiputfile('*.json', '保存PR数据为JSON', ...
                    sprintf('PR_%s_%s.json', prData.issueNumber, char(datetime('now', 'Format', 'yyyyMMdd_HHmmss'))));
                
                if isequal(filename, 0) || isequal(pathname, 0)
                    % figure(app.MainFigure) #tmpfix;
                    return; % 用户取消
                end
                
                filePath = fullfile(pathname, filename);
                
                % 显示进度条（遵循第8条规则）
                d = uiprogressdlg(dialogFig, 'Title', '导出JSON', ...
                    'Message', '正在导出PR数据到JSON文件...', 'Indeterminate', 'on');
                
                % 调用业务逻辑服务导出（遵循第4条规则）
                success = PRExportImportService.exportToJSON(prData, filePath);
                
                close(d);
                
                if success
                    uialert(dialogFig, sprintf('✅ JSON导出成功！\n文件保存至：%s', filePath), ...
                        '导出成功', 'Icon', 'success');
                else
                    uialert(dialogFig, '❌ JSON导出失败！', '导出失败', 'Icon', 'error');
                end
                
                % 遵循第8条规则：确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                % 遵循第2条规则：明确报告错误
                uialert(dialogFig, sprintf('导出JSON失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function exportPRToMarkdown(app, dialogFig, projectEdit, issueNumberEdit, reporterDropDown, ...
                sourceEdit, initialSeverityDropDown, problemDescArea, problemMechanismArea, ...
                relatedReqEdit, impactAnalysisArea, assigneeEdit, approverDropDown, ...
                baselineEdit, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox)
            % 导出PR v2.0到Markdown文件（包含checkbox状态）
            try
                % 收集表单数据（15个字段 + 4个checkbox状态）
                prData = app.collectPRFormData(projectEdit, issueNumberEdit, reporterDropDown, ...
                    sourceEdit, initialSeverityDropDown, problemDescArea, problemMechanismArea, ...
                    relatedReqEdit, impactAnalysisArea, assigneeEdit, approverDropDown, ...
                    baselineEdit, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                    baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox);
                
                % 显示文件保存对话框
                [filename, pathname] = uiputfile('*.md', '保存PR数据为Markdown', ...
                    sprintf('PR_%s_%s.md', prData.issueNumber, char(datetime('now', 'Format', 'yyyyMMdd_HHmmss'))));
                
                if isequal(filename, 0) || isequal(pathname, 0)
                    % figure(app.MainFigure) #tmpfix;
                    return; % 用户取消
                end
                
                filePath = fullfile(pathname, filename);
                
                % 显示进度条（遵循第8条规则）
                d = uiprogressdlg(dialogFig, 'Title', '导出Markdown', ...
                    'Message', '正在导出PR数据到Markdown文件...', 'Indeterminate', 'on');
                
                % 调用业务逻辑服务导出（遵循第4条规则）
                success = PRExportImportService.exportToMarkdown(prData, filePath);
                
                close(d);
                
                if success
                    uialert(dialogFig, sprintf('✅ Markdown导出成功！\n文件保存至：%s', filePath), ...
                        '导出成功', 'Icon', 'success');
                else
                    uialert(dialogFig, '❌ Markdown导出失败！', '导出失败', 'Icon', 'error');
                end
                
                % 遵循第8条规则：确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                % 遵循第2条规则：明确报告错误
                uialert(dialogFig, sprintf('导出Markdown失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function importPRFromJSON(app, dialogFig, projectEdit, issueNumberEdit, reporterDropDown, ...
                sourceEdit, initialSeverityDropDown, problemDescArea, problemMechanismArea, ...
                relatedReqEdit, impactAnalysisArea, assigneeEdit, approverDropDown, ...
                baselineEdit, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox)
            % 从JSON文件导入PR v2.0数据（包含checkbox状态恢复）
            try
                % 显示文件选择对话框
                [filename, pathname] = uigetfile('*.json', '选择PR JSON文件');
                
                if isequal(filename, 0) || isequal(pathname, 0)
                    % figure(app.MainFigure) #tmpfix;
                    return; % 用户取消
                end
                
                filePath = fullfile(pathname, filename);
                
                % 显示进度条（遵循第8条规则）
                d = uiprogressdlg(dialogFig, 'Title', '导入JSON', ...
                    'Message', '正在从JSON文件导入PR数据...', 'Indeterminate', 'on');
                
                % 调用业务逻辑服务导入（遵循第4条规则）
                [success, prData] = PRExportImportService.importFromJSON(filePath);
                
                close(d);
                
                if success
                    % 填充表单数据（15个字段 + 4个checkbox状态）
                    app.fillPRFormData(prData, projectEdit, issueNumberEdit, reporterDropDown, ...
                        sourceEdit, initialSeverityDropDown, problemDescArea, problemMechanismArea, ...
                        relatedReqEdit, impactAnalysisArea, assigneeEdit, approverDropDown, ...
                        baselineEdit, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                        baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox);
                    
                    uialert(dialogFig, '✅ JSON导入成功！\n已填充表单数据和checkbox状态', ...
                        '导入成功', 'Icon', 'success');
                else
                    uialert(dialogFig, '❌ JSON导入失败！', '导入失败', 'Icon', 'error');
                end
                
                % 遵循第8条规则：确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                % 遵循第2条规则：明确报告错误
                uialert(dialogFig, sprintf('导入JSON失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function prData = collectPRFormData(~, projectEdit, issueNumberEdit, reporterDropDown, ...
                sourceEdit, initialSeverityDropDown, problemDescArea, problemMechanismArea, ...
                relatedReqEdit, impactAnalysisArea, assigneeEdit, approverDropDown, ...
                baselineEdit, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox)
            % 收集PR v2.0表单数据（15个字段：11必填+4可选 + 4个checkbox状态）
            prData = struct();
            
            % ========== 必填字段（11个）==========
            % 基础信息（5个）
            prData.project = projectEdit.Value;
            prData.issueNumber = issueNumberEdit.Value;
            prData.reporter = reporterDropDown.Value;
            prData.source = sourceEdit.Value;
            prData.initialSeverity = initialSeverityDropDown.Value;
            
            % 问题描述和分析（4个）
            prData.problemDescription = problemDescArea.Value;
            prData.problemMechanism = problemMechanismArea.Value;
            prData.relatedRequirements = relatedReqEdit.Value;
            prData.impactAnalysis = impactAnalysisArea.Value;
            
            % 分配和审批（2个）
            prData.assignee = assigneeEdit.Value;
            prData.approver = approverDropDown.Value;
            
            % ========== 可选字段（4个）==========
            prData.baseline = baselineEdit.Value;
            prData.verificationStatus = verificationArea.Value;
            prData.confirmedSeverity = confirmedSeverityDropDown.Value;
            prData.disposalPlan = disposalPlanArea.Value;
            
            % ========== Checkbox状态（4个）- 用于导入时恢复状态 ==========
            prData.uiState = struct();
            prData.uiState.baselineChecked = baselineCheckbox.Value;
            prData.uiState.verificationChecked = verificationCheckbox.Value;
            prData.uiState.confirmedSeverityChecked = confirmedSeverityCheckbox.Value;
            prData.uiState.disposalPlanChecked = disposalPlanCheckbox.Value;
        end
        function fillPRFormData(~, prData, projectEdit, issueNumberEdit, reporterDropDown, ...
                sourceEdit, initialSeverityDropDown, problemDescArea, problemMechanismArea, ...
                relatedReqEdit, impactAnalysisArea, assigneeEdit, approverDropDown, ...
                baselineEdit, verificationArea, confirmedSeverityDropDown, disposalPlanArea, ...
                baselineCheckbox, verificationCheckbox, confirmedSeverityCheckbox, disposalPlanCheckbox)
            % 填充PR v2.0表单数据（15个字段 + 4个checkbox状态）
            
            % ========== 必填字段（11个）==========
            if isfield(prData, 'project')
                projectEdit.Value = prData.project;
            end
            if isfield(prData, 'issueNumber')
                issueNumberEdit.Value = prData.issueNumber;
            end
            if isfield(prData, 'reporter')
                reporterDropDown.Value = prData.reporter;
            end
            if isfield(prData, 'source')
                sourceEdit.Value = prData.source;
            end
            if isfield(prData, 'initialSeverity')
                initialSeverityDropDown.Value = prData.initialSeverity;
            end
            if isfield(prData, 'problemDescription')
                problemDescArea.Value = prData.problemDescription;
            end
            if isfield(prData, 'problemMechanism')
                problemMechanismArea.Value = prData.problemMechanism;
            end
            if isfield(prData, 'relatedRequirements')
                relatedReqEdit.Value = prData.relatedRequirements;
            end
            if isfield(prData, 'impactAnalysis')
                impactAnalysisArea.Value = prData.impactAnalysis;
            end
            if isfield(prData, 'assignee')
                assigneeEdit.Value = prData.assignee;
            end
            if isfield(prData, 'approver')
                approverDropDown.Value = prData.approver;
            end
            
            % ========== 可选字段（4个）==========
            if isfield(prData, 'baseline')
                baselineEdit.Value = prData.baseline;
            end
            if isfield(prData, 'verificationStatus')
                verificationArea.Value = prData.verificationStatus;
            end
            if isfield(prData, 'confirmedSeverity')
                confirmedSeverityDropDown.Value = prData.confirmedSeverity;
            end
            if isfield(prData, 'disposalPlan')
                disposalPlanArea.Value = prData.disposalPlan;
            end
            
            % ========== 恢复Checkbox状态（4个）==========
            if isfield(prData, 'uiState')
                if isfield(prData.uiState, 'baselineChecked')
                    baselineCheckbox.Value = prData.uiState.baselineChecked;
                end
                if isfield(prData.uiState, 'verificationChecked')
                    verificationCheckbox.Value = prData.uiState.verificationChecked;
                end
                if isfield(prData.uiState, 'confirmedSeverityChecked')
                    confirmedSeverityCheckbox.Value = prData.uiState.confirmedSeverityChecked;
                end
                if isfield(prData.uiState, 'disposalPlanChecked')
                    disposalPlanCheckbox.Value = prData.uiState.disposalPlanChecked;
                end
            end
        end
        
        function loadApproversAsync(app, approverDropDown)
            % 异步加载审批人列表
            try
                % 显示加载进度条
                d = uiprogressdlg(app.MainFigure, 'Title', '加载审批人', ...
                    'Message', '正在从ALM服务器获取审批人列表...', 'Indeterminate', 'on');
                
                % 使用预初始化的ALM集成服务查询审批人
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                [success, approvers] = app.ALMService.getApprovers();
                
                close(d);
                
                if success && ~isempty(approvers)
                    % 更新下拉菜单选项
                    approverDropDown.Items = approvers;
                    
                    % 设置默认选择第一个审批人
                    if ~isempty(approvers)
                        approverDropDown.Value = approvers{1};
                    end
                    
                    fprintf('✅ 成功加载 %d 个审批人\n', length(approvers));
                else
                    % 加载失败时显示错误信息
                    approverDropDown.Items = {'无法获取审批人列表'};
                    approverDropDown.Value = '无法获取审批人列表';
                    
                    % 显示错误提示
                    uialert(app.MainFigure, '无法从ALM服务器获取审批人列表，请检查网络连接或联系管理员', ...
                        '加载审批人失败', 'Icon', 'warning');
                    
                    fprintf('❌ 加载审批人列表失败\n');
                end
                
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('d', 'var')
                    close(d);
                end
                
                % 异常处理
                approverDropDown.Items = {'加载审批人时发生错误'};
                approverDropDown.Value = '加载审批人时发生错误';
                
                uialert(app.MainFigure, sprintf('加载审批人列表时发生错误：%s', ME.message), ...
                    '错误', 'Icon', 'error');
                
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function loadPRApprovalProgress(app, prId, progressField)
            % 异步加载PR审批进度信息
            % 2明确错误处理
            try
                % 使用预初始化的ALM服务查询PR详情
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                
                % 查询PR详情以获取审批进度
                [success, prDetail] = app.ALMService.getPRDetail(prId);
                
                if success && ~isempty(prDetail)
                    % 解析审批进度信息
                    if isfield(prDetail, 'approval_level') && isfield(prDetail, 'required_levels')
                        currentLevel = prDetail.approval_level;
                        totalLevels = prDetail.required_levels;
                        
                        if isfield(prDetail, 'approval') && strcmp(prDetail.approval, 'APPROVED')
                            progressText = sprintf('✅ 已完成所有审批（%d/%d级）', totalLevels, totalLevels);
                        elseif isfield(prDetail, 'approval') && strcmp(prDetail.approval, 'REJECTED')
                            progressText = sprintf('❌ 审批已拒绝（第%d级）', currentLevel);
                        else
                            progressText = sprintf('⏳ 当前：第 %d 级 / 共 %d 级', currentLevel + 1, totalLevels);
                        end
                    else
                        % 没有多级审批信息，显示基本状态
                        if isfield(prDetail, 'approval')
                            switch prDetail.approval
                                case 'APPROVED'
                                    progressText = '✅ 已批准';
                                case 'REJECTED'
                                    progressText = '❌ 已拒绝';
                                case 'PENDING'
                                    progressText = '⏳ 等待审批';
                                otherwise
                                    progressText = sprintf('状态：%s', prDetail.approval);
                            end
                        else
                            progressText = '⏳ 等待审批';
                        end
                    end
                    
                    progressField.Value = progressText;
                    fprintf('✅ PR #%d 审批进度: %s\n', prId, progressText);
                else
                    progressField.Value = '⚠️ 无法加载审批进度';
                    fprintf('❌ 无法加载PR #%d 审批进度\n', prId);
                end
                
            catch ME
                progressField.Value = sprintf('❌ 加载失败: %s', ME.message);
                fprintf('❌ 加载PR #%d 审批进度时出错: %s\n', prId, ME.message);
            end
        end
        
        function loadCRApprovalProgress(app, crId, progressField)
            % 异步加载CR审批进度信息
            % 2明确错误处理
            try
                % 使用预初始化的ALM服务查询CR详情
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                
                % 查询CR详情以获取审批进度
                [success, crDetail] = app.ALMService.getCRDetail(crId);
                
                if success && ~isempty(crDetail)
                    % 解析审批进度信息
                    if isfield(crDetail, 'approval_level') && isfield(crDetail, 'required_levels')
                        currentLevel = crDetail.approval_level;
                        totalLevels = crDetail.required_levels;
                        
                        if isfield(crDetail, 'approval') && strcmp(crDetail.approval, 'APPROVED')
                            progressText = sprintf('✅ 已完成所有审批（%d/%d级）', totalLevels, totalLevels);
                        elseif isfield(crDetail, 'approval') && strcmp(crDetail.approval, 'REJECTED')
                            progressText = sprintf('❌ 审批已拒绝（第%d级）', currentLevel);
                        else
                            progressText = sprintf('⏳ 当前：第 %d 级 / 共 %d 级', currentLevel + 1, totalLevels);
                        end
                    else
                        % 没有多级审批信息，显示基本状态
                        if isfield(crDetail, 'approval')
                            switch crDetail.approval
                                case 'APPROVED'
                                    progressText = '✅ 已批准';
                                case 'REJECTED'
                                    progressText = '❌ 已拒绝';
                                case 'PENDING'
                                    progressText = '⏳ 等待审批';
                                otherwise
                                    progressText = sprintf('状态：%s', crDetail.approval);
                            end
                        else
                            progressText = '⏳ 等待审批';
                        end
                    end
                    
                    progressField.Value = progressText;
                    fprintf('✅ CR #%d 审批进度: %s\n', crId, progressText);
                else
                    progressField.Value = '⚠️ 无法加载审批进度';
                    fprintf('❌ 无法加载CR #%d 审批进度\n', crId);
                end
                
            catch ME
                progressField.Value = sprintf('❌ 加载失败: %s', ME.message);
                fprintf('❌ 加载CR #%d 审批进度时出错: %s\n', crId, ME.message);
            end
        end
        
        function loadUsersAndSetReporter(app, reporterDropDown)
            % 异步加载系统用户列表并设置Reporter默认值
            try
                fprintf('🔄 正在获取系统用户列表...\n');
                
                % 如果ALM服务未初始化，则创建新实例
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                
                % 获取系统用户列表
                [success, userData] = app.ALMService.getUsers();
                
                if success && ~isempty(userData) && isfield(userData, 'users')
                    % 提取用户名列表
                    userList = cell(length(userData.users), 1);
                    for i = 1:length(userData.users)
                        userList{i} = userData.users(i).username;
                    end
                    
                    % 更新下拉菜单选项
                    reporterDropDown.Items = userList;
                    
                    % 设置默认值为当前用户
                    if ~isempty(app.userName) && any(strcmp(userList, app.userName))
                        reporterDropDown.Value = app.userName;
                    elseif ~isempty(userList)
                        reporterDropDown.Value = userList{1}; % 如果当前用户不在列表中，使用第一个用户
                    end
                    
                    fprintf('✅ 成功获取用户列表，共%d个用户\n', length(userList));
                    
                else
                    % 获取用户列表失败，但允许手动输入
                    fprintf('⚠️ 无法获取用户列表，但允许手动输入\n');
                    reporterDropDown.Items = {'获取用户列表失败'};
                    
                    % 设置默认值为当前用户（如果有的话）
                    if ~isempty(app.userName)
                        reporterDropDown.Value = app.userName;
                    else
                        reporterDropDown.Value = '请输入提报人';
                    end
                end
                
            catch ME
                fprintf('❌ 加载用户列表异常: %s\n', ME.message);
                
                % 异常情况下也允许手动输入
                reporterDropDown.Items = {'加载失败-请手动输入'};
                if ~isempty(app.userName)
                    reporterDropDown.Value = app.userName;
                else
                    reporterDropDown.Value = '请输入提报人';
                end
            end
        end
        
        function loadUsersAsync(app, reporterDropDown, assigneeDropDown)
            % 异步加载系统用户列表，同时更新reporter和assignee下拉菜单
            % 3从服务器动态加载用户清单
            % SRS追溯: F-U-05
            try
                fprintf('🔄 正在获取系统用户列表（用于报告人和分配人员）...\n');
                
                % 如果ALM服务未初始化，则创建新实例
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                
                % 获取系统用户列表
                [success, userData] = app.ALMService.getUsers();
                
                if success && ~isempty(userData) && isfield(userData, 'users')
                    % 提取用户名列表
                    userList = cell(length(userData.users), 1);
                    for i = 1:length(userData.users)
                        userList{i} = userData.users(i).username;
                    end
                    
                    % 更新reporter下拉菜单
                    reporterDropDown.Items = userList;
                    if ~isempty(app.userName) && any(strcmp(userList, app.userName))
                        reporterDropDown.Value = app.userName;
                    elseif ~isempty(userList)
                        reporterDropDown.Value = userList{1};
                    end
                    
                    % 更新assignee下拉菜单
                    assigneeDropDown.Items = userList;
                    if ~isempty(userList)
                        assigneeDropDown.Value = userList{1};
                    end
                    
                    fprintf('✅ 成功加载用户列表，共%d个用户\n', length(userList));
                    
                else
                    fprintf('⚠️ 获取用户列表失败\n');
                    reporterDropDown.Items = {'获取用户列表失败'};
                    assigneeDropDown.Items = {'获取用户列表失败'};
                    if ~isempty(app.userName)
                        reporterDropDown.Value = app.userName;
                        assigneeDropDown.Value = app.userName;
                    end
                end
                
            catch ME
                fprintf('❌ 加载用户列表异常: %s\n', ME.message);
                reporterDropDown.Items = {'加载失败'};
                assigneeDropDown.Items = {'加载失败'};
            end
        end
        
        function loadProjectsAsync(app, projectDropDown, baselineDropDown)
            % 异步加载项目清单
            % 3项目名称从服务器动态加载
            % SRS追溯: F-C-08
            try
                fprintf('🔄 正在获取项目清单...\n');
                
                % 如果ALM服务未初始化，则创建新实例
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                
                % 获取项目清单
                [success, projects] = app.ALMService.getProjects();
                
                if success && ~isempty(projects)
                    % 更新项目下拉菜单
                    projectDropDown.Items = projects;
                    projectDropDown.Value = projects{1};  % 默认选择第一个项目
                    
                    fprintf('✅ 成功加载项目清单，共%d个项目\n', length(projects));
                    
                    % 自动加载第一个项目的基线
                    app.loadBaselinesForProject(projects{1}, baselineDropDown);
                    
                else
                    fprintf('⚠️ 获取项目清单失败\n');
                    projectDropDown.Items = {'获取项目列表失败'};
                    projectDropDown.Value = '获取项目列表失败';
                end
                
            catch ME
                fprintf('❌ 加载项目清单异常: %s\n', ME.message);
                projectDropDown.Items = {'加载失败'};
                projectDropDown.Value = '加载失败';
            end
        end
        
        function onProjectChanged(app, projectDropDown, baselineDropDown)
            % 项目变化时的回调：更新基线列表（级联）
            % 3基线根据项目动态加载
            % SRS追溯: F-C-09
            try
                selectedProject = projectDropDown.Value;
                
                if ~isempty(selectedProject) && ~contains(selectedProject, '加载') && ~contains(selectedProject, '失败')
                    fprintf('🔄 项目已变更为: %s，正在加载对应的基线列表...\n', selectedProject);
                    app.loadBaselinesForProject(selectedProject, baselineDropDown);
                end
                
            catch ME
                fprintf('❌ 更新基线列表异常: %s\n', ME.message);
            end
        end
        
        function loadBaselinesForProject(app, projectName, baselineDropDown)
            % 为指定项目加载基线清单
            % 3基线根据项目动态加载
            % SRS追溯: F-C-09
            try
                fprintf('🔄 正在获取项目"%s"的基线清单...\n', projectName);
                
                % 如果ALM服务未初始化，则创建新实例
                % 基于规则第4条：使用ALMIntegrationService替代直接使用ALMCloudService
                if isempty(app.ALMService)
                    app.ALMService = ALMIntegrationService.getInstance('DebugMode', false);
                end
                
                % 获取指定项目的基线清单
                [success, baselines, ~] = app.ALMService.getBaselines(projectName);
                
                if success && ~isempty(baselines)
                    % 添加空选项（可选字段）
                    baselinesWithEmpty = [{'（可选）留空'}, baselines'];
                    baselineDropDown.Items = baselinesWithEmpty;
                    baselineDropDown.Value = '（可选）留空';  % 默认为空
                    
                    fprintf('✅ 成功加载基线清单，共%d个基线\n', length(baselines));
                    
                else
                    fprintf('⚠️ 获取基线清单失败或该项目无基线配置\n');
                    baselineDropDown.Items = {'（可选）留空', '获取基线失败或无配置'};
                    baselineDropDown.Value = '（可选）留空';
                end
                
            catch ME
                fprintf('❌ 加载基线清单异常: %s\n', ME.message);
                baselineDropDown.Items = {'（可选）留空', '加载失败'};
                baselineDropDown.Value = '（可选）留空';
            end
        end
        
        %% CR导出/导入函数（遵循第4条规则：调用CRExportImportService）
        function exportCRToJSON(app, dialogFig, ticketNumberEdit, reporterDropDown, approverDropDown, ...
                changeDescArea, projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                baselineEdit, analysisEdit, relatedPREdit, ...
                projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                baselineCheckbox, analysisCheckbox, relatedPRCheckbox)
            % 导出CR到JSON文件（包含checkbox状态）
            try
                % 收集表单数据（4必填 + 7可选 + 7个checkbox状态）
                crData = app.collectCRFormData(ticketNumberEdit, reporterDropDown, approverDropDown, ...
                    changeDescArea, projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                    baselineEdit, analysisEdit, relatedPREdit, ...
                    projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                    baselineCheckbox, analysisCheckbox, relatedPRCheckbox);
                
                % 显示文件保存对话框
                [filename, pathname] = uiputfile('*.json', '保存CR数据为JSON', ...
                    sprintf('CR_%s_%s.json', crData.ticketNumber, char(datetime('now', 'Format', 'yyyyMMdd_HHmmss'))));
                
                if isequal(filename, 0) || isequal(pathname, 0)
                    % figure(app.MainFigure) #tmpfix;
                    return; % 用户取消
                end
                
                filePath = fullfile(pathname, filename);
                
                % 显示进度条（遵循第8条规则）
                d = uiprogressdlg(dialogFig, 'Title', '导出JSON', ...
                    'Message', '正在导出CR数据到JSON文件...', 'Indeterminate', 'on');
                
                % 调用业务逻辑服务导出（遵循第4条规则）
                success = CRExportImportService.exportToJSON(crData, filePath);
                
                close(d);
                
                if success
                    uialert(dialogFig, sprintf('✅ JSON导出成功！\n文件保存至：%s', filePath), ...
                        '导出成功', 'Icon', 'success');
                else
                    uialert(dialogFig, '❌ JSON导出失败！', '导出失败', 'Icon', 'error');
                end
                
                % 遵循第8条规则：确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                % 遵循第2条规则：明确报告错误
                uialert(dialogFig, sprintf('导出JSON失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function exportCRToMarkdown(app, dialogFig, ticketNumberEdit, reporterDropDown, approverDropDown, ...
                changeDescArea, projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                baselineEdit, analysisEdit, relatedPREdit, ...
                projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                baselineCheckbox, analysisCheckbox, relatedPRCheckbox)
            % 导出CR到Markdown文件（包含checkbox状态，只导出勾选的字段）
            try
                % 收集表单数据（4必填 + 7可选 + 7个checkbox状态）
                crData = app.collectCRFormData(ticketNumberEdit, reporterDropDown, approverDropDown, ...
                    changeDescArea, projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                    baselineEdit, analysisEdit, relatedPREdit, ...
                    projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                    baselineCheckbox, analysisCheckbox, relatedPRCheckbox);
                
                % 显示文件保存对话框
                [filename, pathname] = uiputfile('*.md', '保存CR数据为Markdown', ...
                    sprintf('CR_%s_%s.md', crData.ticketNumber, char(datetime('now', 'Format', 'yyyyMMdd_HHmmss'))));
                
                if isequal(filename, 0) || isequal(pathname, 0)
                    % figure(app.MainFigure) #tmpfix;
                    return; % 用户取消
                end
                
                filePath = fullfile(pathname, filename);
                
                % 显示进度条（遵循第8条规则）
                d = uiprogressdlg(dialogFig, 'Title', '导出Markdown', ...
                    'Message', '正在导出CR数据到Markdown文件...', 'Indeterminate', 'on');
                
                % 调用业务逻辑服务导出（遵循第4条规则）
                success = CRExportImportService.exportToMarkdown(crData, filePath);
                
                close(d);
                
                if success
                    uialert(dialogFig, sprintf('✅ Markdown导出成功！\n文件保存至：%s', filePath), ...
                        '导出成功', 'Icon', 'success');
                else
                    uialert(dialogFig, '❌ Markdown导出失败！', '导出失败', 'Icon', 'error');
                end
                
                % 遵循第8条规则：确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                % 遵循第2条规则：明确报告错误
                uialert(dialogFig, sprintf('导出Markdown失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function importCRFromJSON(app, dialogFig, ticketNumberEdit, reporterDropDown, approverDropDown, ...
                changeDescArea, projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                baselineEdit, analysisEdit, relatedPREdit, ...
                projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                baselineCheckbox, analysisCheckbox, relatedPRCheckbox)
            % 从JSON文件导入CR数据（包含checkbox状态恢复）
            try
                % 显示文件选择对话框
                [filename, pathname] = uigetfile('*.json', '选择CR JSON文件');
                
                if isequal(filename, 0) || isequal(pathname, 0)
                    % figure(app.MainFigure) #tmpfix;
                    return; % 用户取消
                end
                
                filePath = fullfile(pathname, filename);
                
                % 显示进度条（遵循第8条规则）
                d = uiprogressdlg(dialogFig, 'Title', '导入JSON', ...
                    'Message', '正在从JSON文件导入CR数据...', 'Indeterminate', 'on');
                
                % 调用业务逻辑服务导入（遵循第4条规则）
                [success, crData] = CRExportImportService.importFromJSON(filePath);
                
                close(d);
                
                if success
                    % 填充表单数据（4必填 + 7可选 + 7个checkbox状态）
                    app.fillCRFormData(crData, ticketNumberEdit, reporterDropDown, approverDropDown, ...
                        changeDescArea, projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                        baselineEdit, analysisEdit, relatedPREdit, ...
                        projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                        baselineCheckbox, analysisCheckbox, relatedPRCheckbox);
                    
                    uialert(dialogFig, '✅ JSON导入成功！\n已填充表单数据和checkbox状态', ...
                        '导入成功', 'Icon', 'success');
                else
                    uialert(dialogFig, '❌ JSON导入失败！', '导入失败', 'Icon', 'error');
                end
                
                % 遵循第8条规则：确保主窗口回到前台
                % figure(app.MainFigure) #tmpfix;
                
            catch ME
                if exist('d', 'var') && isvalid(d)
                    close(d);
                end
                % 遵循第2条规则：明确报告错误
                uialert(dialogFig, sprintf('导入JSON失败：%s', ME.message), '错误', 'Icon', 'error');
                % figure(app.MainFigure) #tmpfix;
            end
        end
        
        function crData = collectCRFormData(~, ticketNumberEdit, reporterDropDown, approverDropDown, ...
                changeDescArea, projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                baselineEdit, analysisEdit, relatedPREdit, ...
                projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                baselineCheckbox, analysisCheckbox, relatedPRCheckbox)
            % 收集CR表单数据（4必填+7可选字段 + 7个checkbox状态）
            crData = struct();
            
            % ========== 必填字段（4个）==========
            crData.ticketNumber = ticketNumberEdit.Value;
            crData.reporter = reporterDropDown.Value;
            crData.approver = approverDropDown.Value;
            crData.changeDescription = changeDescArea.Value;
            
            % ========== 可选字段（7个）==========
            if ~isempty(projectEdit.Value)
                crData.project = projectEdit.Value;
            end
            if ~isempty(assigneeEdit.Value)
                crData.assignee = assigneeEdit.Value;
            end
            if ~isempty(changeSourceEdit.Value)
                crData.changeSource = changeSourceEdit.Value;
            end
            if ~isempty(baselineEdit.Value)
                crData.baseline = baselineEdit.Value;
            end
            if ~isempty(analysisEdit.Value)
                crData.impactAnalysis = analysisEdit.Value;
            end
            if relatedPREdit.Value > 0
                crData.relatedPRId = relatedPREdit.Value;
            end
            % reportDate处理
            if ~isempty(reportDatePicker.Value)
                crData.reportDate = char(reportDatePicker.Value, 'yyyy-MM-dd');
            end
            
            % ========== Checkbox状态（7个）- 用于导入时恢复状态 ==========
            crData.uiState = struct();
            crData.uiState.projectChecked = projectCheckbox.Value;
            crData.uiState.assigneeChecked = assigneeCheckbox.Value;
            crData.uiState.reportDateChecked = reportDateCheckbox.Value;
            crData.uiState.changeSourceChecked = changeSourceCheckbox.Value;
            crData.uiState.baselineChecked = baselineCheckbox.Value;
            crData.uiState.analysisChecked = analysisCheckbox.Value;
            crData.uiState.relatedPRChecked = relatedPRCheckbox.Value;
        end
        
        function fillCRFormData(~, crData, ticketNumberEdit, reporterDropDown, approverDropDown, ...
                changeDescArea, projectEdit, assigneeEdit, reportDatePicker, changeSourceEdit, ...
                baselineEdit, analysisEdit, relatedPREdit, ...
                projectCheckbox, assigneeCheckbox, reportDateCheckbox, changeSourceCheckbox, ...
                baselineCheckbox, analysisCheckbox, relatedPRCheckbox)
            % 填充CR表单数据（4必填 + 7可选 + 7个checkbox状态）
            
            % 必填字段
            if isfield(crData, 'ticketNumber')
                ticketNumberEdit.Value = crData.ticketNumber;
            end
            if isfield(crData, 'reporter')
                reporterDropDown.Value = crData.reporter;
            end
            if isfield(crData, 'approver')
                approverDropDown.Value = crData.approver;
            end
            if isfield(crData, 'changeDescription')
                changeDescArea.Value = crData.changeDescription;
            end
            
            % 可选字段
            if isfield(crData, 'project')
                projectEdit.Value = crData.project;
            end
            if isfield(crData, 'assignee')
                assigneeEdit.Value = crData.assignee;
            end
            if isfield(crData, 'changeSource')
                changeSourceEdit.Value = crData.changeSource;
            end
            if isfield(crData, 'baseline')
                baselineEdit.Value = crData.baseline;
            end
            if isfield(crData, 'impactAnalysis')
                analysisEdit.Value = crData.impactAnalysis;
            end
            if isfield(crData, 'relatedPRId')
                relatedPREdit.Value = crData.relatedPRId;
            end
            if isfield(crData, 'reportDate')
                try
                    reportDatePicker.Value = datetime(crData.reportDate, 'InputFormat', 'yyyy-MM-dd');
                catch
                    reportDatePicker.Value = datetime('today');
            end
        end
        
            % ========== 恢复Checkbox状态（7个）==========
            if isfield(crData, 'uiState')
                if isfield(crData.uiState, 'projectChecked')
                    projectCheckbox.Value = crData.uiState.projectChecked;
                end
                if isfield(crData.uiState, 'assigneeChecked')
                    assigneeCheckbox.Value = crData.uiState.assigneeChecked;
                end
                if isfield(crData.uiState, 'reportDateChecked')
                    reportDateCheckbox.Value = crData.uiState.reportDateChecked;
                end
                if isfield(crData.uiState, 'changeSourceChecked')
                    changeSourceCheckbox.Value = crData.uiState.changeSourceChecked;
                end
                if isfield(crData.uiState, 'baselineChecked')
                    baselineCheckbox.Value = crData.uiState.baselineChecked;
                end
                if isfield(crData.uiState, 'analysisChecked')
                    analysisCheckbox.Value = crData.uiState.analysisChecked;
                end
                if isfield(crData.uiState, 'relatedPRChecked')
                    relatedPRCheckbox.Value = crData.uiState.relatedPRChecked;
                end
            end
        end
        
    end
end